Skip to content
Esc
navigateopen⌘Jpreview
Dashboard
On this page

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

UI type

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:

Select the public tenant from the tenant management page and then click on Add new provider in the Social/Enterprise Providers section

Social/Enterprise providers

Select Add Custom Provider option

New Provider

Fill in the details as shown below and click on Save

OAuth2 provider
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

Social/Enterprise providers

Select Add Custom Provider option

New Provider

Fill in the details as shown below and click on Save

OAuth2 provider
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, clients configuration.
  • 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.FromIdTokenPayload map.
  • You can also add the UserInfoMap.FromUserInfoAPI map 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:

  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

API reference

API schema and response details