Custom providers
Add custom authentication providers to SuperTokens
Overview
If you can’t find a provider in the built-in list, you can add your own custom implementation as shown below.
Create a custom provider
1. Render the authentication method in the authentication UI
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>
),
},
],
// ...
},
// ...
}),
// ...
],
});You need to build your own UI listing the buttons for each of the social login providers you want your users to use. See the implementation details page for what to do after a user clicks one of the buttons.
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:
Select the public tenant from the tenant management page and then click on Add new provider in the Social/Enterprise Providers section

Select Add Custom Provider option

Fill in the details as shown below and click on Save

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",
},
},
},
},
],
},
}),
],
});import (
"github.com/supertokens/supertokens-golang/recipe/thirdparty"
"github.com/supertokens/supertokens-golang/recipe/thirdparty/tpmodels"
"github.com/supertokens/supertokens-golang/supertokens"
)
func main() {
supertokens.Init(supertokens.TypeInput{
RecipeList: []supertokens.Recipe{
thirdparty.Init(&tpmodels.TypeInput{
SignInAndUpFeature: tpmodels.TypeInputSignInAndUp{
Providers: []tpmodels.ProviderInput{
{
Config: tpmodels.ProviderConfig{
ThirdPartyId: "custom",
Name: "Custom provider",
Clients: []tpmodels.ProviderClientConfig{
{
ClientID: "...",
ClientSecret: "...",
Scope: []string{"profile", "email"},
},
},
AuthorizationEndpoint: "https://example.com/oauth/authorize",
AuthorizationEndpointQueryParams: map[string]interface{}{ // optional
"someKey1": "value1",
"someKey2": nil,
},
TokenEndpoint: "https://example.com/oauth/token",
TokenEndpointBodyParams: map[string]interface{}{ // optional
"someKey1": "value1",
},
UserInfoEndpoint: "https://example.com/oauth/userinfo",
UserInfoMap: tpmodels.TypeUserInfoMap{
FromIdTokenPayload: struct {
UserId string "json:\"userId,omitempty\""
Email string "json:\"email,omitempty\""
EmailVerified string "json:\"emailVerified,omitempty\""
}{
UserId: "id",
Email: "email",
EmailVerified: "email_verified",
},
},
},
},
},
},
}),
},
})
}from supertokens_python import init, InputAppInfo
from supertokens_python.recipe import thirdparty
from supertokens_python.recipe.thirdparty import ProviderInput, ProviderConfig, ProviderClientConfig, SignInAndUpFeature
from supertokens_python.recipe.thirdparty.provider import UserInfoMap, UserFields
init(
app_info=InputAppInfo(api_domain="...", app_name="...", website_domain="..."),
framework='...',
recipe_list=[
thirdparty.init(
sign_in_and_up_feature=SignInAndUpFeature(
providers=[
ProviderInput(
config=ProviderConfig(
third_party_id="custom",
name="Custom Provider",
clients=[
ProviderClientConfig(
client_id="...",
client_secret="...",
scope=["email", "profile"],
),
],
authorization_endpoint="https://example.com/oauth/authorize",
authorization_endpoint_query_params={
"someKey1": "value1",
"someKey2": None,
},
token_endpoint="https://example.com/oauth/token",
token_endpoint_body_params={
"someKey1": "value1",
},
user_info_endpoint="https://example.com/oauth/userinfo",
user_info_map=UserInfoMap(
from_id_token_payload=UserFields(
user_id="id",
email="email",
email_verified="email_verified",
),
from_user_info_api=UserFields(),
),
),
),
]
)
)
]
)curl --location --request PUT '<CORE_API_ENDPOINT>/public/recipe/multitenancy/config/thirdparty' \
--header 'api-key: <YOUR_API_KEY>' \
--header 'Content-Type: application/json' \
--data-raw '{
"config": {
"thirdPartyId": "custom",
"name": "Custom provider",
"clients": [{
"clientId": "...",
"clientSecret": "...",
"scope": ["email", "profile"]
}],
"authorizationEndpoint": "https://example.com/oauth/authorize",
"authorizationEndpointQueryParams": {
"someKey1": "value1",
"someKey2": "value2"
},
"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 Field | Description | Example |
|---|---|---|
thirdPartyId |
Unique identifier for the provider | For Google: "google" |
name |
Display name used on the frontend login button | Setting "XYZ" shows “Login using XYZ” |
clients |
Array of client credentials for frontend clients | Multiple entries needed for web/mobile apps with different credentials. Include clientType if using multiple clients |
AuthorizationEndpoint |
URL for user login | Google: "https://accounts.google.com/o/oauth2/v2/auth" |
AuthorizationEndpointQueryParams |
Optional configuration to modify query parameters | Can add, modify, or remove (using null) query params |
TokenEndpoint |
API endpoint for exchanging Authorization Code | Google: "https://oauth2.googleapis.com/token" |
TokenEndpointBodyParams |
Optional configuration to modify request body | Can add, modify, or remove (using null) body params |
UserInfoEndpoint |
API endpoint for getting user information | Google: "https://www.googleapis.com/oauth2/v1/userinfo" |
UserInfoMap.FromUserInfoAPI |
Maps provider’s JSON response fields to user info | Example mapping:userId: "id"email: "email"emailVerified: "email_verified"For nested values use: userId: "user.id" |
If the provider is Open ID Connect (OIDC) compatible, you can provide URL for the OIDCDiscoverEndpoint configuration. The backend SDK automatically discovers authorization endpoint, token endpoint and the user info endpoint by querying the <OIDCDiscoverEndpoint>/.well-known/openid-configuration.
Below is an example of how to set the OIDC discovery endpoint:
Select the public tenant from the tenant management page and then click on Add new provider in the Social/Enterprise Providers section

Select Add Custom Provider option

Fill in the details as shown below and click on Save

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"],
},
],
oidcDiscoveryEndpoint: "https://example.com/.well-known/openid-configuration",
authorizationEndpointQueryParams: {
someKey1: "value1",
someKey2: null,
},
userInfoMap: {
fromIdTokenPayload: {
userId: "id",
email: "email",
emailVerified: "email_verified",
},
},
},
},
],
},
}),
],
});import (
"github.com/supertokens/supertokens-golang/recipe/thirdparty"
"github.com/supertokens/supertokens-golang/recipe/thirdparty/tpmodels"
"github.com/supertokens/supertokens-golang/supertokens"
)
func main() {
supertokens.Init(supertokens.TypeInput{
RecipeList: []supertokens.Recipe{
thirdparty.Init(&tpmodels.TypeInput{
SignInAndUpFeature: tpmodels.TypeInputSignInAndUp{
Providers: []tpmodels.ProviderInput{
{
Config: tpmodels.ProviderConfig{
ThirdPartyId: "custom",
Name: "Custom provider",
Clients: []tpmodels.ProviderClientConfig{
{
ClientID: "...",
ClientSecret: "...",
Scope: []string{"profile", "email"},
},
},
OIDCDiscoveryEndpoint: "https://example.com/.well-known/openid-configuration",
AuthorizationEndpointQueryParams: map[string]interface{}{ // optional
"someKey1": "value1",
"someKey2": nil,
},
UserInfoMap: tpmodels.TypeUserInfoMap{
FromIdTokenPayload: struct {
UserId string "json:\"userId,omitempty\""
Email string "json:\"email,omitempty\""
EmailVerified string "json:\"emailVerified,omitempty\""
}{
UserId: "id",
Email: "email",
EmailVerified: "email_verified",
},
},
},
},
},
},
}),
},
})
}from supertokens_python import init, InputAppInfo
from supertokens_python.recipe import thirdparty
from supertokens_python.recipe.thirdparty import ProviderInput, ProviderConfig, ProviderClientConfig, SignInAndUpFeature
from supertokens_python.recipe.thirdparty.provider import UserInfoMap, UserFields
init(
app_info=InputAppInfo(api_domain="...", app_name="...", website_domain="..."),
framework='...',
recipe_list=[
thirdparty.init(
sign_in_and_up_feature=SignInAndUpFeature(
providers=[
ProviderInput(
config=ProviderConfig(
third_party_id="custom",
name="Custom Provider",
clients=[
ProviderClientConfig(
client_id="...",
client_secret="...",
scope=["email", "profile"],
),
],
oidc_discovery_endpoint="https://example.com/.well-known/openid-configuration",
authorization_endpoint_query_params={
"someKey1": "value1",
"someKey2": None,
},
user_info_map=UserInfoMap(
from_user_info_api=UserFields(
user_id="id",
email="email",
email_verified="email_verified",
),
from_id_token_payload=UserFields(),
),
),
),
]
)
)
]
)curl --location --request PUT '<CORE_API_ENDPOINT>/public/recipe/multitenancy/config/thirdparty' \
--header 'api-key: <YOUR_API_KEY>' \
--header 'Content-Type: application/json' \
--data-raw '{
"config": {
"thirdPartyId": "custom",
"name": "Custom provider",
"clients": [{
"clientId": "...",
"clientSecret": "...",
"scope": ["email", "profile"]
}],
"oidcDiscoveryEndpoint": "https://example.com/.well-known/openid-configuration",
"authorizationEndpointQueryParams": {
"someKey1": "value1",
"someKey2": "value2"
},
"userInfoMap": {
"fromIdTokenPayload": {
"userId": "id",
"email": "email",
"emailVerified": "email_verified"
}
}
}
}'- The configuration values are similar to the ones in the “Via OAuth endpoints” method. Please read that section to understand the
thirdPartyId,name,clientsconfiguration. - Unlike the “Via OAuth endpoints”, you can obtain the user’s info from the ID token payload using the configuration specified by you in the
UserInfoMap.FromIdTokenPayloadmap. - You can also add the
UserInfoMap.FromUserInfoAPImap as done in the “Via OAuth endpoints” section. SuperTokens auto merges the user information.
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: Parameters<typeof originalImplementation.getUserInfo>[0]) {
// Call provider's APIs to get profile info
// ...
return {
thirdPartyUserId: "...",
email: {
id: "...",
isVerified: true,
},
rawUserInfoFromProvider: {
fromUserInfoAPI: {
first_name: "...",
last_name: "...",
},
},
};
},
};
},
},
],
},
}),
],
});import (
"github.com/supertokens/supertokens-golang/recipe/thirdparty"
"github.com/supertokens/supertokens-golang/recipe/thirdparty/tpmodels"
"github.com/supertokens/supertokens-golang/supertokens"
)
func main() {
supertokens.Init(supertokens.TypeInput{
RecipeList: []supertokens.Recipe{
thirdparty.Init(&tpmodels.TypeInput{
SignInAndUpFeature: tpmodels.TypeInputSignInAndUp{
Providers: []tpmodels.ProviderInput{
{
Config: tpmodels.ProviderConfig{
ThirdPartyId: "custom",
Clients: []tpmodels.ProviderClientConfig{
{
ClientID: "...",
ClientSecret: "...",
Scope: []string{"profile", "email"},
},
},
AuthorizationEndpoint: "https://example.com/oauth/authorize",
AuthorizationEndpointQueryParams: map[string]interface{}{
"response_type": "token", // Changing an existing parameter
"response_mode": "form", // Adding a new parameter
"scope": nil, // Removing a parameter
},
TokenEndpoint: "https://example.com/oauth/token",
},
Override: func(originalImplementation *tpmodels.TypeProvider) *tpmodels.TypeProvider {
// ...
originalImplementation.GetUserInfo = func(oAuthTokens map[string]interface{}, userContext *map[string]interface{}) (tpmodels.TypeUserInfo, error) {
// Call provider's APIs to get profile info
// ...
return tpmodels.TypeUserInfo{
ThirdPartyUserId: "...",
Email: &tpmodels.EmailStruct{
ID: "...",
IsVerified: true,
},
RawUserInfoFromProvider: tpmodels.TypeRawUserInfoFromProvider{
FromUserInfoAPI: map[string]interface{}{
"first_name": "...",
"last_name": "...",
// ...
},
},
}, nil
}
return originalImplementation
},
},
},
},
}),
},
})
}from supertokens_python import init, InputAppInfo
from supertokens_python.recipe import thirdparty
from supertokens_python.recipe.thirdparty.provider import ProviderClientConfig, ProviderConfig, ProviderInput, Provider
from supertokens_python.recipe.thirdparty import SignInAndUpFeature
from supertokens_python.recipe.thirdparty.types import UserInfo, UserInfoEmail, RawUserInfoFromProvider
from typing import Dict, Any
def override_custom_provider(provider: Provider) -> Provider:
async def get_user_info(oauth_tokens: Dict[str, Any], user_context: Dict[str, Any]) -> UserInfo:
return UserInfo(
third_party_user_id="...",
email=UserInfoEmail(
email="...",
is_verified=True,
),
raw_user_info_from_provider=RawUserInfoFromProvider(
from_id_token_payload={},
from_user_info_api={
"first_name": "...",
"last_name": "...",
},
),
)
provider.get_user_info = get_user_info
return provider
init(
app_info=InputAppInfo(api_domain="...", app_name="...", website_domain="..."),
framework='...',
recipe_list=[
thirdparty.init(
sign_in_and_up_feature=SignInAndUpFeature(
providers=[
ProviderInput(
config=ProviderConfig(
third_party_id="custom",
clients=[
ProviderClientConfig(
client_id="...",
client_secret="...",
scope=["profile", "email"],
),
],
authorization_endpoint="https://example.com/oauth/authorize",
authorization_endpoint_query_params={
"response_type": "token", # Changing an existing parameter
"response_mode": "form", # Adding a new parameter
"scope": None, # Removing a parameter
},
token_endpoint="https://example.com/oauth/token",
),
override=override_custom_provider
),
]
)
)
]
)The original implementation has 4 functions which can be overridden:
-
GetConfigForClientTypeSelects 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_hintappears in the request, you can add it to theAuthorizationEndpointQueryParamsby overriding this function. -
GetAuthorisationRedirectURLThis function returns the full URL (along with query params) to which the user needs to navigate to log in.
-
ExchangeAuthCodeForOAuthTokensThis function is responsible for exchanging one time use
Authorization Codewith the user’s tokens, such asAccess Token,ID Token, etc. -
GetUserInfoThis function is responsible for fetching the user information such as
UserId,EmailandEmailVerifiedusing the tokens returned from the previous function.