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:
Request an access code
Exchange the access code for an authorization token
Send an authorized request to an API
As a sequence, the basic authorization code flow will look something like this:

Settings
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 Authorization
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.
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 Exchange
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 URI
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 pagePass the generic URL as the
redirect_uri
param to the third party authorize pageUser 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
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
请登录或注册以提交评论。