There are two basic types of apps - those that need access to user and/or site data, and those that do not. Apps that don’t require data access are simply installed and updated when a new version is released, but they can’t access the Weebly API. Those that need to access user/site data do so via the Weebly API, and require site owner authorization to do so. This authorization is handled by an OAuth2 flow (our implementation follows the standard OAuth 2.0 as specified in RFC 6749). This flow shows site owners the data the app needs to access, and when they agree, the flow issues an access token to the app that allows it to access that site’s data via the Weebly API. ​ The OAuth flow also happens when the permissions in a new version of an app are different from the previous one. And apps can also have these permissions revoked, either by changing the website to which they’re installed, (for example by copying a website that has an authorized app), or explicitly by the developer. ​

Install Flow Overview

​When a site owner goes to install an app that requires access to a site’s data, a modal displays showing requested permissions based on data the app needs, and whether the app needs read or write access. You configure these permissions as scopes in your app’s manifest.

OAuth connection dialog

When the site owner clicks Connect, the site owner’s browser is directed to your website, using the callback URL you’ve configured in the manifest. The URL passes in parameters that identify the site owner (user_id) and the site (site_id), along with an HMAC-SHA256 hash used for authorization. You implement server code that decodes the hash, verifies it, and then redirects back to Weebly using the URL sent in the hash.

In this redirect, you can include an alternate URL to use for the rest of the OAuth flow, along with (in very special conditions) additional permission scopes.

If there are additional scopes, the site owner sees another authorization modal. Once accepted (or if there are no additional scopes, once received), Weebly returns an authorization code. You redirect to the URL in the request with your app’s secret. Weebly verifies the secret and returns a permanent access token. This token is valid only for this particular site owner, site, and set of scopes approved during the flow. You store this code and use it whenever the app needs to access the API. At this point, the app has access to the API.

You redirect to the provided callback_url with your client_id in order to retrieve the final destination of the flow. You configure this destination in the app’s manifest. For example, you might choose to end the flow in the editor. The app is now considered connected.

Now you can use the token to retrieve permissible information. For example, say you configured the final destination to be your site, displayed in a takeover within Weebly. You might now retrieve site owner information in order to create an account at your app’s site and log the site owner in.

Weebly OAuth process flow

Implement the OAuth Flow

Follow these procedures to implement the OAuth flow for your app.

Declare Scopes

​ Most API calls require a specific scope permission. For example, if you want to retrieve site data, your app needs the read:site scope permission. You define the required scopes in the app’s manifest.

  • Review the Weebly API to determine the data your app needs access to.
  • In the manifest, add an array of needed scopes. List only the scopes needed, but note that scopes are explicit and do not inherit from each other. For example, write access does not imply read access. Choose from the following scopes (note that all OAuth apps are implicitly given access to the read:user scope):​​
    • read:blog: The app can read blog information.
    • write:blog: The app can modify blog information.
    • read:site: The app can read site information.
    • write:site: The app can modify site information.
    • read:store-catalog: The app can read store information.
    • write:store-catalog: The app can modify store information.
    • read:store-orders: The app can read order information.
    • write:store-orders: The app can modify order information.
    • read:membership: The app can read site’s member and group information. ​ Here’s an example of an app that needs to read and write to site information:
{
    "manifest": "1",
    "version": "1.2",
    "locale": "",
    "client_id" : "12345",
    "scopes": ["read:site", "write:site"],
    ...
}


Configure Your Callback URL

​The callback_url is also defined in the manifest, and is where Weebly sends responses during the OAuth flow. You can overwrite this with a redirect once you decode and verify the hash. The URL must use the HTTPS protocol.

Here’s an example callback_url entry in the manifest:

{
    "manifest": "1",
    "version": "1.2",
    "locale": "",
    "client_id" : "12345",
    "callback_url" : "https://your_rad_site.com/weebly/authorize",
    "scopes": ["read:site", "write:site"],
    ...
}


Configure a Final Destination

Also set in the manifest, the final destination is where your site owner ends up once the OAuth flow is complete. Set oauth_final_destination to one of the following:

  • editor: site owner is returned to the editor, and if the app is an element, it’s highlighted in the element tray.
    Newly installed app in the editor tray
  • publish: site owner is returned to the editor and they are asked to publish the site. Ending a flow this way is very helpful when the app is a snippet and the only way it becomes active is by publishing the site.
  • ​manage: site owner is returned to the manage_app_url configured in the manifest (more info here).
  • dashboard_card-<dashboard_card_name>: If your app has a dashboard card, instead of ending the flow at your external site, you can end the flow in your dashboard card’s takeover screen, where the site owner can configure their app. For example, if you named your dashboard card My App Card, the value would be dashboard_card-My_App_Card.

Here’s an example of an app whose endpoint is the editor:

{
    "manifest": "1",
    "version": "1.2",
    "locale": "",
    "client_id" : "12345",
    "callback_url" : "https://your_rad_site.com/weebly/authorize",
    "scopes": ["read:site", "write:site"],
    "oauth_final_destination" : "editor",
    ...
}

Decode and Verify the Hash

​Once the site owner approves the scope of information your app requested, Weebly sends an HMAC hash (encoded using HMAC-SHA256) of these URL parameters to the callback URL provided in the manifest:

  • user_id: Weebly site owner id
  • site_id: Weebly site id
  • timestamp: Current timestamp
  • hmac: The user_id, site_id (if provided), and timestamp processed through HMAC-SHA256 using the app’s secret key
  • callback_url: The Weebly URL endpoint to be used in your callbacks. Do not hard code this value, as it is subject to change.

Your server code needs to decode the HMAC hash using the app’s secret (app secrets are created when registering the app), and then compare it to the sent parameters.

​Weebly sends the following:

[MANIFEST_CALLBACK_URL]?user_id=[user_ID]&timestamp=[TIMESTAMP]&site_id=[SITE_ID]&
hmac=[HMAC-SHA256]&callback_url=[NEXT_OAUTH_STEP_URL]

 You decode and verify the hash:

$hash_string = "user_id=[user_ID]&timestamp=[TIMESTAMP]&site_id=[SITE_ID]";
$hash = hash_hmac('sha256', $hash_string, SECRET_KEY);

 Here’s an excerpt from the php-webhook-client sample app showing validation of the hash and redirecting to the URL sent by Weebly in the hash (see the complete application code here):

/**
* Utility class for HMAC functions in this context (from HMAC.php)
*/
class HMAC {
    /**
    * Check if payload hashed with secret matches the expected hash
    *
    * @param $payload
    * @param $secret
    * @param $current_hash
    * @return bool
    */
    public static function isHmacValid($payload, $secret, $current_hash) {
        $verification_hash = hash_hmac('sha256', $payload, $secret, false);
        return ($verification_hash == $current_hash);
    }


Redirect Back to Weebly for an Authorization Code

Once you verify the hash, you redirect back to the callback_url sent in the hash. In this return call, you can request additional permission scopes as request parameters (along with parameters that identify you and the site owner/site installing the app, and an optional redirect URL).

If you do add scopes, these scopes are compared to the permission scopes defined in the manifest and already granted. If they are the same, the flow continues. If there are different ones, the Weebly site displays another modal, showing the additional requested permissions.

Include the following parameters in your request:

  • client_id: Your ID number, found on your Developer Admin portal page
  • user_id: passed to you in the original request
  • site_id: ID of the site, passed to you in the original request
  • scope: comma-delimited list of scopes of information that your app needs. Initial/global scopes should be listed in the manifest and not here. The scopes added here are compared with those in the manifest. If they are different, then the site owner will be shown a dialog asking for the additional permissions.

    Choose from the following scopes:

    • read:blog: The app can read blog information.
    • write:blog: The app can modify blog information.
    • read:site: The app can read site information.
    • write:site: The app can modify site information.
    • read:store-catalog: The app can read store information.
    • write:store-catalog: The app can modify store information.
    • read:store-orders: The app can read order information.
    • write:store-orders: The app can modify order information.
    • read:membership: The app can read site’s member and group information.
    • write:membership: The app can modify site’s member and group information. ​
  • redirect_uri: The URI for the resulting page. This URI must use the https protocol. This can be used to override the callback_url in the manifest. If you don’t provide a URI, then the site owner is directed to the URI defined in the manifest.
  • version: The version number passed to you in the original request. Note that non-approved versions (that is versions that are not yet in the App Center) can only be installed by the site owner that owns the app.

Use this API call: GET /app-center/oauth/authorize

[CALLBACK_URL]?client_id=[CLIENT_ID]&user_id=[user_ID]&site_id=[SITE_ID]&scope=[SCOPES]&redirect_uri=[REDIRECT_URI]&version=[VERSION]


Here’s code from the PHP sample:

/**
* Returns the url to redirect to for oauth authentication (Step 1 in OAuth flow)
*
* @param (optional) array $scope           An array of the permissions your application is
*                                          requesting i.e (read:user, read:commerce)
* @param (optional) string $redirect_uri   The url weebly will redirect to upon site owner's grant of
*                                          permissions. Defaults to application callback url
* @param (optional) string $callback_url   The url provided by weebly to initiate the authorize
*                                          step of the oauth process
*
*
* @return string $authorization_url
*/
public function getAuthorizationUrl($scope=array(), $redirect_uri=null, $callback_url=null)
{
    if (isset($callback_url) === true) {
        $authorization_url = $callback_url;
    } else {
        $authorization_url = self::WEEBLY_DOMAIN.'/app-center/oauth/authorize';
    }
    $parameters = '?client_id='.$this->client_id.'&user_id='.$this->user_id;
    if (isset($this->site_id) === true) {
        $parameters .= '&site_id='.$this->site_id;
    }
    if (isset($redirect_uri) === true) {
        $parameters .= '&redirect_uri='.$redirect_uri;
    }
    if (is_array($scope) === true && count($scope) > 0) {
        $scope_parameters = implode(',', $scope);
        $parameters .= '&scope='.$scope_parameters;
    }
    if (isset($this->version)) {
        $parameters .= '&version='.$this->version;
    }
  return $authorization_url.$parameters;
}



Redirect Back to Weebly to Trade Auth Code for Permanent Token

Weebly responds back to the callback URL in the manifest (or the redirect URL if provided in the last call) with an authorization code.

The following parameters are returned:

  • user_id: Weebly site owner ID
  • timestamp: Current timestamp
  • site_id: Weebly site ID
  • authorization_code: The authorization code that you will trade for a permanent access token
  • callback_url: The URL for the next step in the flow

After receiving the authorization code, you post that authorization code plus the app’s secret back to Weebly to obtain a permanent token by making a POST request with the following JSON parameters:

  • client_id: Application client ID found on your Developer Admin portal page
  • client_secret: App secret key found on your Developer Admin portal page
  • authorization_code: Authorization code returned from the last step

​Use this method: ​POST /app-center/oauth/access_token

{ "client_id": "[APPLICATION_CLIENT_ID]", "client_secret": "[APPLICATION_SECRET]", "authorization_code": "[AUTHORIZATION_CODE]"}

 Excerpt from the PHP sample: Trade Authorization Code for Permanent Token - from Applicaiton.php

/**
* Handle the /oauth/phase_two route
*
* @param $params GET params passed via URL
*/
private function oauthPhaseTwo($params) {
// build client instance
    $wc = new WeeblyClient($this->client_id, $this->client_secret, $params['user_id'], $params['site_id'], null, $params['version']);
    // get token from auth code
    $token = $wc->getAccessToken($params['authorization_code'], $params['callback_url']);
    // check for a valid return
    if($token->access_token !== null) {
        // token retrieved
        /**
         * Note: If you were going to make API calls on behalf of your application,
         * this is where you would want to store the access_token for reuse later.
    }
}

 Trade Authorization Code for a Permanent Token - from WeeblyClient.php

/**
* Exchanges a temporary authorization code for a permanent access_token
*
* @param string $authorization_code        The authorization_code sent from weebly after the site owner has
*                                          granted the application access to their data.
* @param (optional) string $callback_url   The url provided by weebly to retrieve the access token
*
*
* @return string    $access_token
*/
public function getAccessToken($authorization_code, $callback_url=null)
{
    if (isset($callback_url) === true) {
        $url = $callback_url;
    } else {
        $url = self::WEEBLY_DOMAIN.'/app-center/oauth/access_token';
    }
    $result = $this->makeRequest($url, $this->prepareAccessTokenParams($authorization_code));
    return $result;
}



Redirect to Final Destination

When the secret and authorization code are verified, Weebly responds with the following:

  • access_token: the permanent token​
  • callback_url: The final endpoint, as configured in the manifest.

Once you receive the token, you have access to the Weebly API for this site. You now redirect to the final destination using the callback URL sent in the previous reply. The app is now considered connected. As long as the app is connected it can continue to access data via the Weebly API.

​Here’s code from the example:

Redirect to Final Destination

*/
    // redirect to final endpoint
    header("location: {$token->callback_url}");
    } else {
        // unable to retrieve access_token
        // display error from server
        if($token->error !== null) {
            echo "<h3>Error: " . $token->error . "</h3>";
        } else {
            echo "<h3>Error: Unable to get Access Token</h3>";
    }



Authorizing a New Version of an App

Access tokens are valid for a given site, app, and scope, so if you change the requested scope, you will need to reauthenticate. For example, if in an upgrade to an app requires additional data access, you must add the needed scopes to the manifest. The next time the site owner attempts to use the app, a modal displays asking for the additional permissions.

Reconnecting after an upgrade requires additional scopes

Using a Token to Access the API

Once the app has the token, it can access the API by sending the token in an HTTP authorization header. For example, this code gets details about a site with the ID of 4321 that belongs to the site owner identified by the token.

curl --request GET \
    --url https://api.weebly.com/v1/user/sites/4321 \
    --header 'accept: application/vnd.weebly.v1+json' \
    --header 'content-type: application/json' \
    --header 'x-weebly-access-token: [YOUR_TOKEN]"

 More info about using the API here.

Disconnecting an App and Forcing Reauthorization

Apps can become disconnected for the following reasons:

  • A new site was created by copying an existing site. Authorization is site-specific. If a site owner creates a new site by copying a site with your app installed, the app will appear in the site, but will be in a disconnected state because it’s now on a different site. The site owner will be asked to reconnect the app on the new site and when they do, a new access token specific to the new site will be granted. You’ll need to store this new token.
  • An update to an app asks for additional permissions. If you publish a new version of your app, and that version requires more permissions, then the app becomes disconnected. The site owner will be asked to reconnect the app by granting the new permissions when they go to use the updated version.
  • You explicitly disconnect the app. You can explicitly disconnect permissions from the site for the app using the App Deauthorize API. When the site owner tries to work with your app, they will be asked to re-grant permissions, which reruns the OAuth flow to get a new token. If something needs to happen before the he can reconnect (for example, payment needs to be received or a social media token is refreshed), you’ll need to add that functionality on your end. If you don’t, the site owner can simply reconnect the app the next time they go to use it. ​

When an app is disconnected, any associated elements or other app artifacts are still displayed in the editor. However, when the site owner tries to work with the app, they are shown a modal asking them to reconnect. Disconnected apps also appear on the My Apps page with button asking the site owner to reconnect. ​​Once he reconnects by granting permissions, the app becomes connected.

Reconnect the app dialog

​Disconnected apps will not function properly on a published page if they rely on an OAuth token, as it will no longer be valid. In the case of an upgrade, the app only becomes disconnected between the time the site owner accesses the app in Weebly (Site Home or the Editor) and they reconnect. Before that, the published site continues to use the last installed version.


Help make these docs better!