info-beamer OAuth2 support
About
info-beamer supports OAuth2 authorization. This allows easy integration of third-party apps and fully uses the existing permission system. Apps can request access to data for an info-beamer account. Once access is granted an app can control some aspects of an info-beamer account on behalf of a user. They can implement features not currently provided by info-beamer itself.
Data access is fine-grained. An app should request just the permissions required to fullfill its task. The info-beamer user granting access is in complete control and can review requested permission prior to granting them to a third-party app.
Granted permissions are handled as account access (similar to granting access to an account to other info-beamer users) and are shown on the account permission page. Users can revoke, review or even customize the granted permissions.
OAuth2 support
info-beamer implements the OAuth2 authentication token and code flow. It's highly recommended that you use the code flow if possible. info-beamer doesn't use client secrets. Instead it implements the PKCE extension for a secure code/access token exchange.
Assuming the code flow, the general idea is as follows:
- A user visits a third-party page implementing an info-beamer app.
- The user clicks on a Authorize access button on that page.
- They are redirected to info-beamer.com
- info-beamer.com will show then a consent page. This page shows the permissions requested by the app and allows the info-beamer user to choose whether to grant or reject access to their account. This step is skipped if the user previously granted access.
- If the user grants access, they are redirected back to the app page. A authorization code is included in the redirect Url.
- The app page issues an HTTP request to info-beamer.com and exchanges the authorization code with an access token.
- The app uses that access token to issue API request to the info-beamer API on behalf of the info-beamer user.
Creating a new app
You can manage your OAuth apps in your permission management screen. Note that you currently cannot yet make your app publicly available. Get in contact with support for that. While your app might not be publicly available yet you can still test everything by authorizing your own account.
Add authentication to a custom app
You'll need the client ID and must have configured a valid redirect uri back to your app. If a user of your app is ready to allow access to their account, your app can initiate the authentication flow required to retrieve an access token.
Requesting authorization
The first steps is to request authorization from the info-beamer.com user by redirecting to the info-beamer.com authorization endpoint.
GET https://info-beamer.com/oauth/authorize
The following request parameters are required:
client_id | String | The client ID of your app. |
response_type | String | Use code for the code authorization flow. |
scope | String | A space separated list of scopes request by your app. See below for a detailed description of supported scopes. |
redirect_uri | String | One of the configured redirect Urls of your app. On success or failure info-beamer.com will redirect back to the provided Url. |
state | String | A one-time randomly chosen value for each authorization request. info-beamer.com will include this value when redirecting back to your app. You must check if the provided value matches the one sent here to ensure that you're the one initiating the authorization request. The state value must be at least 8 characters long. |
code_challenge | String | The base64_urlsafe(sha256(code_verifier)).strip("=") value for a one-time randomly chosen code_verifier value. You'll have to memorize the code_verifier value as it's required later when you exchange the authorization code for an access token. |
code_challenge_method | String | Should be S256, indicating your app is using the SHA256 code_challenge method described above. |
The user is now either using an existing login session for their info-beamer account or can log in to their account. They will then be presented with a consent page describing the requested access given by the values provided in the scope parameter. Alternatively if the user previously granted access to the app, the consent screen is skipped and access is granted automatically.
Alternatively and app might redirect to the authorization endpoint and include additional scope values not requested in earlier authorization requests. This might happen if your app requires additional permission. In that case the user is shown the consent screen again and can choose if they want to grant or reject any additional permissions requested.
If they grant access, info-beamer redirects back the the Url provided in redirect_uri with the following query parameters added:
code | String | An opaque authorization code. This code can be exchanged with an access token. See below. |
state | String | The state provided when initially redirecting to info-beamer. You must check if the state provided here matches the one you used when initially redirecting to info-beamer.com. If the values do not match, you must abort the authorization process as a third party is trying to mess with the user. |
In case of an error or if the user or the info-beamer system denies the request, the following paramters are returned instead:
error | String | An error code indicating the reason for the failed authorization request. See here. |
error_description | String | A reason for the returned error. This value can be presented to the app user. |
Retrieving the access token
The code returned to your app by info-beamer in case of a successful authorization isn't useful on its own. It cannot be used to issue API calls. You'll have to exchange it for an access token within 15 seconds first. This works as follows:
POST https://info-beamer.com/oauth/token
You'll have to include the following POST parameters when issuing this request:
client_id | String | The client ID of your app. |
grant_type | String | Must be authorization_code. |
redirect_uri | String | The redirect_uri you initially provided when starting the authorization flow. |
code | String | The value of code submitted to you by the redirection back to your app. |
code_verifier | String | The value of code_verifier used in the initial authorization redirect. Submitting this value here proofs that the initial redirect originated from your app and prevents anyone else to issue this call. This is useful in case the authorization flow is used on mobile devices and a rogue app registered as a handler of your redirect_uri. Without this additional verification, such an app could retrieve the access token. |
You'll get a JSON response with the following values:
.access_token | String | A session based API key that your app can use to issue API calls on behalf of the user. |
.token_type | String | "bearer" |
.expires_in | Integer | The number of seconds this access token is valid. |
.scope | String | The granted scopes. If the user modified the app's access and assigned a customm ACL the value custom will be returned. Otherwise a space separated list of granted scopes will be returned. |
.refresh_token | String | A refresh token that can be use to retrieve additional access tokens for the same authorization. This field isn't returned by default. Contact support if you want to use refresh tokens in your app. |
Refresh tokens
If your initial authorization code exchange returns a refresh_token value, you can exchange that for another access token later. Use the same token exchange API endpoint for that as follows:
POST https://info-beamer.com/oauth/token
You'll have to include the following POST parameters when issuing this request:
grant_type | String | Must be refresh_token. |
refresh_token | String | The value of the refresh token. |
You'll get a JSON response with the following values:
.access_token | String | A session based API key that your app can use to issue API calls on behalf of the user. |
.token_type | String | "bearer" |
.expires_in | Integer | The number of seconds this access token is valid. |
Scopes
One major feature of OAuth2 is that an app can request only the permissions required to do its job. As a user of an app, it's not required to give an app full access to your account. An app the helps manage a user's devices should not have access their assets for example.
An app developer can specify the required permissions as "scopes". The info-beamer permission system already uses policies and scopes are mapped to policies. If you open up the policy list you'll see that some policies have an oauth scope. An app can request each of them by including the scope in its authorization request. The value of scope in the initial authorization redirect might look like this:
asset:read setup:full
If the user grants access to their account, the returned access token would be granted the two policies specified by asset:read and setup:full. You can check each policy to see what access they allow. In the example the returned access token would allow the app to query asset information and create, modify or delete setups.
An example authorization flow
Here's a full example of what a flow might look like. Once the user click on a button labeled (for example) "Access your info-beamer account", the app redirects to the following Url (Line breaks for illustration purposes only):
https://info-beamer.com/oauth/authorize ?response_type=code &client_id=4e2b0589b577dcf817c3714c4217c174 &state=204789b5d63624681d2777356374f8c9cd3426413e4c4b121136d129 &scope=device%3Aread%20check &redirect_uri=https%3A%2F%2Flexample.net%2F &code_challenge=sIPbr43EmYOnu7aCb1rJH_KWtX0ifHw59aJf985ZBR0 &code_challenge_method=S256
The state value is randomly chosen by the app and must be unique for each authorization flow. You'll see further down how the code_challenge value was generated.
The user is now present a screen where they can chose to allow or deny this authorization request. The two requested permissions are given in the value of scope and in this example are device:read as well as check:
If the user grants access, they are directed back to the Url given in redirect_uri with two parameters added. This request looks like this:
https://example.net/ ?code=00b4f5c25b827c56f81fe7cb03ad7058 &state=204789b5d63624681d2777356374f8c9cd3426413e4c4b121136d129
The app must now check if the value of state matches the one it included in the initial redirect. If they don't match, the authorization must be aborted as that means that someone else (probably an attacker) started the authorization flow.
If the state matches, the app can now send a POST request to exchange the code for an access token. This request must send the following values as POST parameters (Line breaks in the POST body are again for illustration purposes only):
POST /oauth/token HTTP/1.1 Host: info-beamer.com Content-Type: application/x-www-form-urlencoded grant_type=authorization_code& code=00b4f5c25b827c56f81fe7cb03ad7058& client_id=4e2b0589b577dcf817c3714c4217c174& redirect_uri=https://example.net/& code_verifier=0156b7f6e38568f0f09ca94cbdc809c41a6ec87bacaf9279dc834621
The code is the one provided in the redirect back to the app. The redirect_uri must match the value used in the initial authorization flow redirect. Finally the code_verifier is the proof that your app initially started the authorization flow.
The code_verifier is a value your app randomly generated when initially redirecting to the authorization page. Remember the code_challenge included in the initial request? Its value is derived from the code_verifier. So instead of including the code_verifier in the initial request you include a derived value that only your app knows later. This prevents the "Authorization Code Interception Attack" problem. The derivation works by generating the SHA256 value of the code_verifier and encoding it with url safe base64 and removing any trailing = padding characters:
# Example Python code: import hashlib, base64 print(base64.urlsafe_b64encode(hashlib.sha256( b'0156b7f6e38568f0f09ca94cbdc809c41a6ec87bacaf9279dc834621' ).digest()).rstrip(b'=')) # will output sIPbr43EmYOnu7aCb1rJH_KWtX0ifHw59aJf985ZBR0
The /oauth/token POST request then returns the following JSON value:
{ "access_token": "SESSION-a0410213573bc3bb5223d39ec4712d2c", "expires_in": 604800, "scope": "check device:read", "token_type": "bearer" }
Your app can now use the access_token to send API requests to the info-beamer API. You also know that this access token will expire in 604800 seconds. The returned scope values shows you which permissions the user granted to your app. If the value is "custom", the user modified the app access with their own custom access.