跳到主要内容

OAuth Flows

在 Apps 中
作者列表
已发布: 2022年5月17日|最后更新: 2022年9月13日

We have provided a basic OAuth2 flow that will allow you to authorize the use of third party resources/APIs. We currently support the following grant types:

  • Authorization Code / 3LO Grant - this requires that the auth process has a "third step" to exchange the authorization code for an access + refresh token

  • Implicit Grant - this grant type can be done without a backend process, as the access token is provided implicitly and does not require user redirection

In this example, let's set up an Authorization Code grant type so that we can:

  1. Request an access code

  2. Exchange the access code for an authorization token

  3. Send an authorized request to an API

As a sequence, the basic authorization code flow will look something like this:

deskpro-custom-app-oauth2-sequence.png

SettingsCopy link to Settings to clipboard

Firstly, we'll need to create some settings to store the client key and secret. The client key will be sent with the initial authorization request and the secret will be a backend only setting used to authorize the access code -> authorization token exchange. In your app manifest.json, add the following settings.

// ... "settings": { "client_key": { "title": "Client Key", "type": "string", "isRequired": true, "isBackendOnly": false }, "secret": { "title": "Secret", "type": "string", "isRequired": true, "isBackendOnly": true } }, //...
copy

Obtaining AuthorizationCopy link to Obtaining Authorization to clipboard

Next, let's add a "Sign-in" button to your app. The sign-in button will actually link to the third party authorization page and should generally contain the client_key and callback_uri. The callback_uri will be provided by Deskpro and it's what we'll use to "listen" for the access token when the user grants access.

import { useState } from "react"; import { useDeskproAppEvents, useDeskproOAuth2Auth } from "@deskpro/app-sdk"; export const Main = () => { const [context, setContext] = useState<Context|null>(null); // Store the app context in state so we can access settings useDeskproAppEvents({ onChange: setContext, }); // Get a callback URL using the name of the eventual state var used to store the access token and the regular expression used to acquire it const { callback } = useDeskproOAuth2Auth("mytoken", /#code=(?<token>[0-9a-f]{20})$/); // When the user clicks the "sign-in" button, we'll need to poll for the access token const onSignIn = () => { callback?.poll().then((props) => console.log("Success! We have an access token in state accessible as:", props)); }; return ( <> { callback && context && ( <a href={`https://example.com/authorize?client_key=${context.settings.client_key}&redirect_uri=${callback.callbackUrl}`} target="_blank" onClick={onSignIn}> Sign-In </a> ) } </> ); };
copy

Ok, there's a lot happening in the example above so let's break it down.

First we need to get the app context so we can access the settings object and get our client_key as we'll need it to form the authorization URL.

Next, we use the useDeskproOAuth2Auth() hook to create a callback URL, passing a name for the access token and a regular expression to acquire the access code from the authorization redirect.

Note

The acquisition regex is used to extract the access code from the page that the user is redirected to after they've authorized the grant request. For example, given the following URL:

https://mysite.deskpro.com/apps-oauth/oauth2/c3817d694/callback#code=29d2ef
copy

the regular expression might look like this:

/#code=(?<token>[0-9a-f]{6})$/
copy

The regular expression group that actually captures the access code or token must have the group name token

Token ExchangeCopy link to Token Exchange to clipboard

Now that we have an access code, we can exchange it for an actual auth token that we can then use to make third party API requests. Let's add a little more code to the onSignIn() function from the previous example.

import { useDeskproClient, proxyFetch, ... } from "@deskpro/app-sdk"; const { client } = useDeskproClient(); const onSignIn = () => { callback?.poll() .then(() => proxyFetch(client)) .then((fetch) => fetch("https://example.com/token?secret=__secret__&access_code=[user[oauth2/mytoken]]")) .then((response) => response.json()) .then((data) => { console.log("Success! We now have an auth token:", data); return client.setUserState<string>("oauth2/token", data.token, { backend: true, expires: ... }); }) ; };
copy

Let's break down the token exchange example.

Firstly, the poll() promise will resolve when we have an access code available in user state, with the placeholder name [user[oauth2/mytoken]]. We can now use this placeholder, along with our secret we have stored in a setting, to exchange the access code for a token.

To do this, send a request to the authorization provder API using both the __secret__ setting placeholder and our newly acquired [user[oauth2/mytoken]] state palceholder.

The response that we now get from the authentication provider should contain our authorization token that we can then use later to request resources from the thirdy party API. So, let's store that in backend-only user state so we can pass it along with authorized API requests.

Now, let's make an actual third party API request:

import { useInitialisedDeskproAppClient, dpFetch } from "@deskpro/app-sdk"; export const Example = () => { useInitialisedDeskproAppClient((client) => { dpFetch(client) .then((fetch) => fetch("https://example.com/api/some-resource", { headers: { "Authorization": "Bearer [user[oauth2/token]]" } })) ; }); };
copy

Strict OAuth Flow Redirect URICopy link to Strict OAuth Flow Redirect URI to clipboard

Sometimes a third party service requires an OAuth flow where the redirect_uri (callback URL) must be provided when setting up the app. A fully qualified URL for the callback must be declared, so it isn't possible to provide redurect URLs that contain random paths generated at runtime - as these would no longer be valid. Our solution for this is to have the user themselves provide a redirect URL when setting up the app with the third party. Brielfy, the process contains these steps:

  • Generate a random key to identify the invoking user

  • Request a generic redirect URL from Deskpro

  • Pass the random key as the state param to the third party authorize page

  • Pass the generic URL as the redirect_uri param to the third party authorize page

  • User visits the third party authorize page

  • Your app polls for a response as it did using the previous OAuth method

  • The User is redirected to the redirect URI (callback URL) where Deskpro validates the state param as well as colelcts the access token or access code

Note

This flow is only available to admin apps, i.e. apps that are integrated with your app's settings form either by app_embedded or app_modal settings types. More details about these field types can be found in the settings section.

First, create a random key we can use for the auth process, we recommend using the UUID library for this.

import { v4 as uuidv4 } from "uuid"; // ... const key = () => uuidv4();
copy

Next, let's initialize the OAuth flow and get our callback URL and poll promise, sotring them in state. The important part here is that we must also pass two regular expressions; one to capture the auth code/token and the other to capture the state param contaning our generated key (so Deskpro can validate the auth flow for the current user)

import { useState } from "react"; import { useInitialisedDeskproAppClient } from "@deskpro/app-sdk"; // ... const [ callbackUrl, setCallbackUrl ] = useState<string|null>(null); const [ poll, setPoll ] = useState<(() => Promise<{ token: string }>)|null>(null); // ... useInitialisedDeskproAppClient((client) => { (async () => { const { callbackUrl, poll } = await client.oauth2().getAdminGenericCallbackUrl( key, /\?code=(?<token>.+?)&/, // RegEx to match access code/token in redirefcted URL /&state=(?<key>.+)/ // RegEx to match state (our key to validate the auth flow) in redirefcted URL ); setCallbackUrl(callbackUrl); setPoll(() => poll); })(); }, [key]);
copy

Finally, we can start polling for the returned auth code/token when the user clicks the link to visit the third party authorize page:

// ... const signIn = () => { if (poll) { poll().then((response) => { // We can now use the returned auth code to perform a 3LO process or // if the access token is returned here, we have successfully authorized the user console.log(response.token); }); } }; return ( <a href={`https://example.com/authorize?state=${key}&redirect_uri=${callbackUrl}&client_id=...`} target="_blank" onClick={signIn} > Sign-in </a> );
copy
有帮助没有帮助

2 人中 1 人认为这个页面有帮助

下一个页面Testing
上一个页面Blocking Mode

请登录或注册以提交评论。