Skip to main content

Custom providers

Overview

If you can't find a provider in the built-in list, you can add your own custom implementation as shown below.

Note

If you think that SuperTokens should support this provider by default, make sure to let the team know on GitHub.

Create a custom provider

1. Render the authentication method in the authentication UI

What type of UI are you using?

Include the provider in the providers array in the frontend SDK.

import React from "react";
import SuperTokens from "supertokens-auth-react";
import ThirdParty from "supertokens-auth-react/recipe/thirdparty";
SuperTokens.init({
appInfo: {
apiDomain: "...",
appName: "...",
websiteDomain: "..."
},
recipeList: [
ThirdParty.init({
signInAndUpFeature: {
providers: [
{
id: "custom",
name: "X", // Will display "Continue with X"

// optional
// you do not need to add a click handler to this as
// we add it for you automatically.
buttonComponent: (props: {name: string}) => <div style={{
cursor: "pointer",
border: "1",
paddingTop: "5px",
paddingBottom: "5px",
borderRadius: "5px",
borderStyle: "solid"
}}>{"Login with " + props.name}</div>
}
],
// ...
},
// ...
}),
// ...
]
});

2. Configure the credentials

You can define a custom provider in a couple of ways. The simplest method is to provide the configuration for the AuthorizationEndpoint, TokenEndpoint, and the mapping for how the user's ID and email from the provider's profile information endpoint. This appears below:

import SuperTokens from "supertokens-node";
import ThirdParty from "supertokens-node/recipe/thirdparty";

SuperTokens.init({
appInfo: {
appName: "...",
apiDomain: "...",
websiteDomain: "...",
},
recipeList: [
ThirdParty.init({
signInAndUpFeature: {
providers: [{
config: {
thirdPartyId: "custom",
name: "Custom provider",
clients: [{
clientId: "...",
clientSecret: "...",
scope: ["profile", "email"]
}],
authorizationEndpoint: "https://example.com/oauth/authorize",
authorizationEndpointQueryParams: {
"someKey1": "value1",
"someKey2": null
},
tokenEndpoint: "https://example.com/oauth/token",
tokenEndpointBodyParams: {
"someKey1": "value1",
},
userInfoEndpoint: "https://example.com/oauth/userinfo",
userInfoMap: {
fromUserInfoAPI: {
userId: "id",
email: "email",
emailVerified: "email_verified",
}
}
}
}]
}
})
]
})
Configuration FieldDescriptionExample
thirdPartyIdUnique identifier for the providerFor Google: "google"
nameDisplay name used on the frontend login buttonSetting "XYZ" shows "Login using XYZ"
clientsArray of client credentials for frontend clientsMultiple entries needed for web/mobile apps with different credentials. Include clientType if using multiple clients
AuthorizationEndpointURL for user loginGoogle: "https://accounts.google.com/o/oauth2/v2/auth"
AuthorizationEndpointQueryParamsOptional configuration to modify query parametersCan add, modify, or remove (using null) query params
TokenEndpointAPI endpoint for exchanging Authorization CodeGoogle: "https://oauth2.googleapis.com/token"
TokenEndpointBodyParamsOptional configuration to modify request bodyCan add, modify, or remove (using null) body params
UserInfoEndpointAPI endpoint for getting user informationGoogle: "https://www.googleapis.com/oauth2/v1/userinfo"
UserInfoMap.FromUserInfoAPIMaps provider's JSON response fields to user infoExample mapping:
userId: "id"
email: "email"
emailVerified: "email_verified"
For nested values use: userId: "user.id"

Handle non standard providers.

Sometimes, one of the steps in the providers interaction may not be per a standard. Therefore, providing the configuration like shown above may not work. To handle this case, you can override any of the steps that happen during the OAuth exchange.

For example, the API call made to get the user's profile info makes a GET call to the UserInfoEndpoint with the user's access token. If your provider requires a different method or requires multiple calls to different endpoints to get the profile info, then you can override the default implementation as shown below:

import SuperTokens from "supertokens-node";
import ThirdParty from "supertokens-node/recipe/thirdparty";

SuperTokens.init({
appInfo: {
appName: "...",
apiDomain: "...",
websiteDomain: "...",
},
recipeList: [
ThirdParty.init({
signInAndUpFeature: {
providers: [{
config: {
thirdPartyId: "custom",
name: "Custom provider",
clients: [{
clientId: "...",
clientSecret: "...",
scope: ["profile", "email"]
}],
authorizationEndpoint: "https://example.com/oauth/authorize",
authorizationEndpointQueryParams: {
"response_type": "token", // Changing an existing parameter
"response_mode": "form", // Adding a new parameter
"scope": null, // Removing a parameter
},
tokenEndpoint: "https://example.com/oauth/token"
},
override: (originalImplementation) => {
return {
...originalImplementation,
getUserInfo: async function (input) {
// Call provider's APIs to get profile info
// ...
return {
thirdPartyUserId: "...",
email: {
id: "...",
isVerified: true
},
rawUserInfoFromProvider: {
fromUserInfoAPI: {
"first_name": "...",
"last_name": "..."
},
}
}
}
}
}
}]
}
})
]
})

The original implementation has 4 functions which can be overridden:

  1. GetConfigForClientType

    Selects the client configuration from the list of clients provided and returns the complete provider configuration. This is a good place to override configuration dynamically. For example, if login_hint appears in the request, you can add it to the AuthorizationEndpointQueryParams by overriding this function.

  2. GetAuthorisationRedirectURL

    This function returns the full URL (along with query params) to which the user needs to navigate to log in.

  3. ExchangeAuthCodeForOAuthTokens

    This function is responsible for exchanging one time use Authorization Code with the user's tokens, such as Access Token, ID Token, etc.

  4. GetUserInfo

    This function is responsible for fetching the user information such as UserId, Email and EmailVerified using the tokens returned from the previous function.

See also