All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
- WebAuthN Recipe
- Adopt the shared reusable lint-pr workflow from supertokens/actions.
- Adopt the shared reusable dev-sync workflow from supertokens/actions.
- Mint a short-lived GitHub App installation token in the release pipeline instead of the ALL_REPO_PAT org secret.
- Replace the release-tag pipeline with a thin caller of the shared reusable release-tag workflow in supertokens/actions.## [0.25.2] - 2026-03-20
- Reuse HTTP client across requests for improved performance
- Add HTTP client override support in querier for custom transport configuration
-
Resolve go vet errors in session JWT parsing, dashboard struct tag, and thirdparty API handler
-
Fix recipe override not being applied for session revocation operations## [0.25.1] - 2024-10-02
-
Adds support for normalizing the connection URI's before returning them in dashboard GET response.
- Removes use of
UserContextin user GET API in dashboard recipe. - Makes optional fields properly optional (i:e the value can be omitted entirely)
- Changes the type of
valueinTypeFormFieldtointerface{}instead ofstringto add support for any type of value in form fields.
To read a value from formFields where id is name,
Before:
// formField should be of type []epmodels.TypeFormField
var name string
for id, value := range formFields {
if id == "name" {
name = value
}
}After:
// formField should be of type []epmodels.TypeFormField
var name string
for id, value := ranger formFields {
if id == "name" {
nameAsStr, asStrOk := value.(string)
if !asStrOk {
// Handle the error accordingly
}
name = nameAsStr
}
}- Fixes memory leak with the JWKS cache.
- Makes optional form fields (in sign up, sign in APIs) truly optional in the request body.
- Improves debug logs for error handlers.
- Adds test server for
backend-sdk-testing - Sends
websiteDomainandapiDomainto core for telemetry. boxyURLis no more mandatory input inadditionalConfigwhile adding boxy-saml provider in thirdParty.- Adds
JWKSRefreshIntervalSecconfig tosessmodels.TypeInputto set the default JWKS cache duration. The default is 4 hours.
- Removes the default
maxAgeInSecondsvalue (previously 300 seconds) in EmailVerification Claim. If the claim value is true andmaxAgeInSecondsis not provided, it will not be refetched. - Updates
NetworkInterceptorfunction signature to returnerroralong with*http.Request - SDK will no longer add
.well-known/openid-configurationto theOIDCDiscoveryEndpointconfig in thirdParty providers. If you have specified any customOIDCDiscoveryEndpointin the ThirdParty.init or added to the core, please make sure to update them to include.well-known/openid-configuration. - For a non-public tenant, when there are no providers added in the core, the SDK used to fallback to the providers added in the ThirdParty.init. Now, the SDK will not fallback to the providers added in the ThirdParty.init by default. If you require a thirdparty provider to be available for non-public tenants, you can make it available by setting
IncludeInNonPublicTenantsByDefaultfor each of the providers. See the migration section below to see how to do this. Note that this only affects non-public tenants when there are no providers added in core.
To make all the providers added in the ThirdParty.init available for non-public tenants by default,
Before:
thirdparty.Init(&tpmodels.TypeInput{
SignInAndUpFeature: tpmodels.TypeInputSignInAndUp{
Providers: []tpmodels.ProviderInput{
{
Config: tpmodels.ProviderConfig{
ThirdPartyId: "google",
// rest of the config
},
},
{
Config: tpmodels.ProviderConfig{
ThirdPartyId: "github",
// rest of the config
},
},
},
},
})After:
thirdparty.Init(&tpmodels.TypeInput{
SignInAndUpFeature: tpmodels.TypeInputSignInAndUp{
Providers: []tpmodels.ProviderInput{
{
Config: tpmodels.ProviderConfig{
ThirdPartyId: "google",
// rest of the config
},
// Add the following line to make this provider available in non-public tenants by default
IncludeInNonPublicTenantsByDefault: true,
},
{
Config: tpmodels.ProviderConfig{
ThirdPartyId: "github",
// rest of the config
},
// Add the following line to make this provider available in non-public tenants by default
IncludeInNonPublicTenantsByDefault: true,
},
},
},
})For each tenant, do the following
-
GET
/appid-<appId>/<tenantId>/recipe/multitenancy/tenant/v2You should see the thirdParty providers in the response using
response.thirdParty.providers -
For each config in providers list, if you have
oidcDiscoveryEndpointin the config, update it to include.well-known/openid-configurationat the end.
Here's a sample code snippet to update the oidcDiscoveryEndpoint:
package main
import (
"log"
"slices"
"strings"
"github.com/supertokens/supertokens-golang/recipe/multitenancy"
)
func isCustomProvider(thirdPartyId string) bool {
customProviders := []string{
"custom",
//... all your custom thirdPartyIds
}
return slices.Contains(customProviders, thirdPartyId)
}
func main() {
tenantRes, err := multitenancy.ListAllTenants()
if err != nil {
log.Fatal(err)
}
tenants := tenantRes.OK.Tenants
for _, tenant := range tenants {
for _, provider := range tenant.ThirdParty.Providers {
if isCustomProvider(provider.ThirdPartyId) && provider.OIDCDiscoveryEndpoint != "" {
provider.OIDCDiscoveryEndpoint = strings.TrimSuffix(provider.OIDCDiscoveryEndpoint, "/")
provider.OIDCDiscoveryEndpoint = provider.OIDCDiscoveryEndpoint + "/.well-known/openid-configuration"
multitenancy.CreateOrUpdateThirdPartyConfig(tenant.TenantId, provider, nil)
}
}
}
}refreshPOSTandRefreshSessionnow clears all user tokens upon CSRF failures and if no tokens are found. See the latest comment on supertokens/supertokens-node#141 for more details.
- The access token cookie expiry has been changed from 100 years to 1 year due to some browsers capping the maximum expiry at 400 days. No action is needed on your part.
- Adds caching per API based on user context.
- Removed ThirdPartyEmailPassword and ThirdPartyPasswordless recipes. Instead, you should use ThirdParty + EmailPassword or ThirdParty + Passwordless recipes separately in your recipe list.
- Removed
ridquery param from:- email verification links
- passwordless magic links
- password reset links
- If
ridheader is present in an API call, the routing no only only depends on that. If the SDK cannot resolve a request handler based on therid, request path and method, it will try to resolve a request handler only based on the request path and method (therefore ignoring theridheader). - New API handlers are:
GET /emailpassword/email/exists=> email password, does email exist API (used to beGET /signup/email/existswithridofemailpasswordorthirdpartyemailpasswordwhich is now deprecated)GET /passwordless/email/exists=> email password, does email exist API (used to beGET /signup/email/existswithridofpasswordlessorthirdpartypasswordlesswhich is now deprecated)GET /passwordless/phonenumber/exists=> email password, does email exist API (used to beGET /signup/phonenumber/existswhich is now deprecated)
- Support for FDI 2.0
-
If you were using
ThirdPartyEmailPassword, you should now initThirdPartyandEmailPasswordrecipes separately. The config for the individual recipes are mostly the same, except the syntax may be different. Check our recipe guides for ThirdParty and EmailPassword for more information. -
If you were using
ThirdPartyPasswordless, you should now initThirdPartyandPasswordlessrecipes separately. The config for the individual recipes are mostly the same, except the syntax may be different. Check our recipe guides for ThirdParty and Passwordless for more information. -
The way to get user information has changed:
-
If you are using
thirdpartyemailpassword.GetUsersByEmail:Before:
userInfo, err := thirdpartyemailpassword.GetUsersByEmail("public", "test@example.com")
After:
thirdPartyUserInfo, err := thirdparty.GetUsersByEmail("public", "test@example.com") if err != nil { // TODO: Handle error } emailPasswordUserInfo, err := emailpassword.GetUserByEmail("public", "test@example.com") if err != nil { // TODO: Handle error } if emailPasswordUserInfo != nil { fmt.Println(emailPasswordUserInfo) } if len(thirdPartyUserInfo) > 0 { fmt.Println(thirdPartyUserInfo) }
-
If you are using
thirdpartyemailpassword.GetUserById:Before:
userInfo, err := thirdpartyemailpassword.GetUserById(userID)
After:
userInfo, err := thirdparty.GetUserByID(userID) if err != nil { // TODO: Handle error } if userInfo == nil { emailPasswordUserInfo, err := emailpassword.GetUserByID(userID) if err != nil { // TODO: Handle error } fmt.Println(emailPasswordUserInfo) } else { fmt.Println(userInfo) }
-
If you are using
thirdpartypasswordless.GetUsersByEmail:Before:
userInfo, err := thirdpartypasswordless.GetUsersByEmail("public", "test@example.com")
After:
thirdPartyUserInfo, err := thirdparty.GetUsersByEmail("public", "test@example.com") if err != nil { return } passwordlessUserInfo, err := passwordless.GetUserByEmail("public", "test@example.com") if err != nil { return } if passwordlessUserInfo != nil { fmt.Println(passwordlessUserInfo) } if len(thirdPartyUserInfo) > 0 { fmt.Println(thirdPartyUserInfo) }
-
If you are using
thirdpartypasswordless.GetUserById:Before:
userInfo, err := thirdpartypasswordless.GetUserById(userID)
After:
userInfo, err := thirdparty.GetUserByID(userID) if err != nil { // TODO: Handle error } if userInfo == nil { passwordlessUserInfo, err := passwordless.GetUserByID(userID) if err != nil { // TODO: Handle error } fmt.Println(passwordlessUserInfo) } else { fmt.Println(userInfo) }
-
- Added
OlderCookieDomainconfig option in the session recipe. This will allow users to clear cookies from the older domain when theCookieDomainis changed. - If
VerifySessiondetects multiple access tokens in the request, it will return a 401 error, prompting a refresh, even if one of the tokens is valid. RefreshPOST(/auth/session/refreshby default) API changes:- now returns 500 error if multiple access tokens are present in the request and
config.OlderCookieDomainis not set. - now clears the access token cookie if it was called without a refresh token (if an access token cookie exists and if using cookie-based sessions).
- now clears cookies from the old domain if
OlderCookieDomainis specified and multiple refresh/access token cookies exist, without updating the front-token or any of the tokens. - now a 200 response may not include new session tokens.
- now returns 500 error if multiple access tokens are present in the request and
- Fixed a bug in the
normaliseSessionScopeOrThrowErrorutil function that caused it to remove leading dots from the scope string.
This update addresses an edge case where changing the CookieDomain config on the server can lead to session integrity issues. For instance, if the API server URL is 'api.example.com' with a cookie domain of '.example.com', and the server updates the cookie domain to 'api.example.com', the client may retain cookies with both '.example.com' and 'api.example.com' domains, resulting in multiple sets of session token cookies existing.
Previously, verifySession would select one of the access tokens from the incoming request. If it chose the older cookie, it would return a 401 status code, prompting a refresh request. However, the RefreshPOST API would then set new session token cookies with the updated CookieDomain, but older cookies will persist, leading to repeated 401 errors and refresh loops.
With this update, verifySession will return a 401 error if it detects multiple access tokens in the request, prompting a refresh request. The RefreshPOST API will clear cookies from the old domain if OlderCookieDomain is specified in the configuration, then return a 200 status. If OlderCookieDomain is not configured, the RefreshPOST API will return a 500 error with a message instructing to set OlderCookieDomain.
Example:
APIDomain: 'api.example.com'CookieDomain: 'api.example.com'
Flow:
- After authentication, the frontend has cookies set with
domain=api.example.com, but the access token has expired. - The server updates
CookieDomainto.example.com. - An API call requiring session with an expired access token (cookie with
domain=api.example.com) results in a 401 response. - The frontend attempts to refresh the session, generating a new access token saved with
domain=.example.com. - The original API call is retried, but because it sends both the old and new cookies, it again results in a 401 response.
- The frontend tries to refresh the session with multiple access tokens:
- If
OlderCookieDomainis not set, the refresh fails with a 500 error.- The user remains stuck until they clear cookies manually or
OlderCookieDomainis set.
- The user remains stuck until they clear cookies manually or
- If
OlderCookieDomainis set, the refresh clears the older cookie, returning a 200 response.- The frontend retries the original API call, sending only the new cookie (
domain=.example.com), resulting in a successful request.
- The frontend retries the original API call, sending only the new cookie (
- If
session.CreateNewSessionnow defaults to the value of thest-auth-modeheader (if available) if the configuredconfig.GetTokenTransferMethodreturnsany.- Enable smooth switching between
useDynamicAccessTokenSigningKeysettings by allowing refresh calls to change the signing key type of a session.
- Make session required during signout.
- Adds a type uint64 to the
accessTokenCookiesExpiryDurationMillislocal variable inrecipe/session/utils.go. It also removes the redundantuint64type forcing needed because of the untyped variable. - Fixes the passing of
tenantIdingetAllSessionHandlesForUserandrevokeAllSessionsForUserbased onfetchAcrossAllTenantsandrevokeAcrossAllTenantsinputs respectively. - Updated fake email generation
- Adds
TLSConfigto SMTP settings. TLSConfigis always passed to gomail so that it can be used when gomail usesSTARTTLSto upgrade the connection to TLS. - #392- Not setting
InsecureSkipVerifytotruein the SMTP settings because it is not recommended to use it in production.
- CI/CD changes
- Updates LinkedIn OAuth implementation as per the latest changes.
- Adds support for configuring multiple frontend domains to be used with the same backend
- Added new
OriginandGetOriginproperties toAppInfo, this can be configured to allow you to conditionally return the value of the frontend domain. This property will replaceWebsiteDomainin a future release ofsupertokens-golang WebsiteDomaininsideAppInfois now optional. UsingOriginorGetOriginis recommended over usingWebsiteDomain. This is not a breaking change and usingWebsiteDomainwill continue to work.
- Fixes github user id conversion to be consistent with other SDKs.
If you were using the SDK Versions >= 0.13.0 and < 0.17.0, use the following override function for github:
{
Config: tpmodels.ProviderConfig{
ThirdPartyId: "github",
// other config
},
Override: func(originalImplementation *tpmodels.TypeProvider) *tpmodels.TypeProvider {
originalGetUserInfo := originalImplementation.GetUserInfo
originalImplementation.GetUserInfo = func(oAuthTokens tpmodels.TypeOAuthTokens, userContext supertokens.UserContext) (tpmodels.TypeUserInfo, error) {
userInfo, err := originalGetUserInfo(oAuthTokens, userContext)
if err != nil {
return userInfo, err
}
number, err := strconv.ParseFloat(userInfo.ThirdPartyUserId, 64)
if err != nil {
return userInfo, err
}
userInfo.ThirdPartyUserId = fmt.Sprint(number)
return userInfo, nil
}
return originalImplementation
},
},If you were using the SDK Versions < 0.13.0, use the following override function for github:
{
Config: tpmodels.ProviderConfig{
ThirdPartyId: "github",
// other config
},
Override: func(originalImplementation *tpmodels.TypeProvider) *tpmodels.TypeProvider {
originalGetUserInfo := originalImplementation.GetUserInfo
originalImplementation.GetUserInfo = func(oAuthTokens tpmodels.TypeOAuthTokens, userContext supertokens.UserContext) (tpmodels.TypeUserInfo, error) {
userInfo, err := originalGetUserInfo(oAuthTokens, userContext)
if err != nil {
return userInfo, err
}
number, err := strconv.ParseFloat(userInfo.ThirdPartyUserId, 64)
if err != nil {
return userInfo, err
}
userInfo.ThirdPartyUserId = fmt.Sprintf("%f", number)
return userInfo, nil
}
return originalImplementation
},
},- Added
NetworkInterceptorto theConnectionInfoconfig. - This can be used to capture/modify all the HTTP requests sent to the core.
- Solves the issue - supertokens/supertokens-core#865
- Adds
debugflag to theTypeInput. If set totrue, debug logs will be printed.
- Fixes an issue where sometimes the
Access-Control-Expose-Headersheader value would contain duplicates
- Fixes an issue where trying to view details of a third party user would show an error for the user not being found when using the thirdpartypasswordless recipe
- Fixes an issue where tenant ids returned for a user from the user get API of the dashboard recipe would always be nil for thirdpartyemailpassword and thirdpartypasswordless recipes
- Added
ValidateAccessTokento the configuration for social login providers, this function allows you to verify the access token returned by the social provider. If you are using Github as a provider, there is a default implementation provided for this function.
- Fixes
timeJoinedcasing in emailpassword and passwordless user objects.
- Solves issue with clock skew during third party sign-in/up - #362
- Bumped
github.com/golang-jtw/jwtversion from v4 to v5. - Bumped
github.com/MicahParks/keyfuncversion from v1 to v2.
- Bumped
- Minimum golang version supported is 1.18
- Adds Twitter/X as a default provider to the third party recipe
- Added a
Cache-Controlheader to/jwt/jwks.json(GetJWKSGET) - Added
ValidityInSecondsto the return value of the overrideableGetJWKSfunction.- This can be used to control the
Cache-Controlheader mentioned above. - It defaults to
60or the value set in the cache-control header returned by the core - This is optional (so you are not required to update your overrides). Returning undefined means that the header is not set.
- This can be used to control the
- Handle AWS Public URLs (ending with
.amazonaws.com) separately while extracting TLDs for SameSite attribute. - Return
500status instead of panic whensupertokens.Middlewareis used without initializing the SDK. - Updates fiber adaptor package in the fiber example.
- The Dashboard recipe now accepts a new
Adminsproperty which can be used to give Dashboard Users write privileges for the user dashboard.
- Dashboard APIs now return a status code
403for all non-GET requests if the currently logged in Dashboard User is not listed in theadminsarray - Now ignoring protected props in the payload in
CreateNewSessionandCreateNewSessionWithoutRequestResponse
- Adds logic to retry network calls if the core returns status 429
- Fixes login methods API to return empty provider array instead of
null - Fixes thirdpartypasswordless initialization when there are no static providers configured
- Added Multitenancy Recipe & always initialized by default.
- Adds Multitenancy support to all the recipes
- Added new Social login providers - LinkedIn
- Added new Multi-tenant SSO providers - Okta, Active Directory, Boxy SAML
- All APIs handled by Supertokens middleware can have an optional
tenantIdprefixed in the path. e.g. //signinup - Following recipe functions have been added:
emailpassword.CreateResetPasswordLinkemailpassword.SendResetPasswordEmailemailverification.CreateEmailVerificationLinkemailverification.SendEmailVerificationEmailthirdparty.GetProviderthirdpartyemailpassword.ThirdPartyGetProviderthirdpartyemailpassword.CreateResetPasswordLinkthirdpartyemailpassword.SendResetPasswordEmailthirdpartypasswordless.ThirdPartyGetProvider
- Only supporting FDI 1.17
- Core must be upgraded to 6.0
- For consistency, all
UnknownUserIDErrorhave been renamed toUnknownUserIdError getUsersOldestFirst&getUsersNewestFirsthas mandatory parametertenantId. Pass'public'if not using multitenancy.- Added mandatory field
tenantIdtoEmailDeliveryInterfaceandSmsDeliveryInterface. Pass'public'if not using multitenancy. - Removed deprecated config
createAndSendCustomEmailandcreateAndSendCustomTextMessage. - EmailPassword recipe changes:
- Added mandatory
tenantIdfield toTypeEmailPasswordPasswordResetEmailDeliveryInput - Removed
resetPasswordUsingTokenFeaturefromTypeInput - Added
tenantIdparam tovalidatefunction inTypeInputFormField - Added mandatory
tenantIdas first parameter to the following recipe index functions:SignUpSignInGetUserByEmailCreateResetPasswordTokenResetPasswordUsingToken
- Added mandatory
tenantIdin the input for the following recipe interface functions. If any of these functions are overridden, they need to be updated accordingly:SignUpSignInGetUserByEmailCreateResetPasswordTokenResetPasswordUsingTokenUpdateEmailOrPassword
- Added mandatory
tenantIdin the input for the following API interface functions. If any of these functions are overridden, they need to be updated accordingly:EmailExistsGETGeneratePasswordResetTokenPOSTPasswordResetPOSTSignInPOSTSignUpPOST
- Added mandatory
- EmailVerification recipe changes:
- Added mandatory
TenantIdfield toEmailVerificationType - Added mandatory
tenantIdas first parameter to the following recipe index functions:CreateEmailVerificationTokenVerifyEmailUsingTokenRevokeEmailVerificationTokens
- Added mandatory
tenantIdin the input for the following recipe interface functions. If any of these functions are overridden, they need to be updated accordingly:CreateEmailVerificationTokenVerifyEmailUsingTokenRevokeEmailVerificationTokens
- Added mandatory
tenantIdin the input for the following API interface functions. If any of these functions are overridden, they need to be updated accordingly:VerifyEmailPOST
- Added mandatory
- Passwordless recipe changes:
- Added
tenantIdparam toValidateEmailAddress,ValidatePhoneNumberandGetCustomUserInputCodefunctions inTypeInput - Added mandatory
TenantIdfield toemaildelivery.PasswordlessLoginTypeandsmsdelivery.PasswordlessLoginType - Added mandatory
tenantIdin the input to the following recipe index functions:CreateCodeWithEmailCreateCodeWithPhoneNumberCreateNewCodeForDeviceConsumeCodeWithUserInputCodeConsumeCodeWithLinkCodeGetUserByEmailGetUserByPhoneNumberRevokeAllCodesByEmailRevokeAllCodesByPhoneNumberRevokeCodeListCodesByEmailListCodesByPhoneNumberListCodesByDeviceIDListCodesByPreAuthSessionIDCreateMagicLinkByEmailCreateMagicLinkByPhoneNumberSignInUpByEmailSignInUpByPhoneNumber
- Added mandatory
tenantIdin the input for the following recipe interface functions. If any of these functions are overridden, they need to be updated accordingly:CreateCodeCreateNewCodeForDeviceConsumeCodeGetUserByEmailGetUserByPhoneNumberRevokeAllCodesRevokeCodeListCodesByEmailListCodesByPhoneNumberListCodesByDeviceIDListCodesByPreAuthSessionID
- Added mandatory
tenantIdin the input for the following API interface functions. If any of these functions are overridden, they need to be updated accordingly:CreateCodePOSTResendCodePOSTConsumeCodePOSTEmailExistsGETPhoneNumberExistsGET
- Added
- ThirdParty recipe changes
- The providers array in
SignInUpFeatureaccepts[]ProviderInputinstead of[]TypeProvider. TypeProvider interface is re-written. Refer migration section for more info. - Removed
SignInUpand addedManuallyCreateOrUpdateUserinstead in the recipe index functions. - Added
ManuallyCreateOrUpdateUserto recipe interface which is being called by the function mentioned above.ManuallyCreateOrUpdateUserrecipe interface function should not be overridden as it is not going to be called by the SDK in the sign in/up flow.SignInUprecipe interface functions is not removed and is being used by the sign in/up flow.
- Added mandatory
tenantIdas first parameter to the following recipe index functions:GetUsersByEmailGetUserByThirdPartyInfo
- Added mandatory
tenantIdin the input for the following recipe interface functions. If any of these functions are overridden, they need to be updated accordingly:GetUsersByEmailGetUserByThirdPartyInfoSignInUp
- Added mandatory
tenantIdin the input for the following API interface functions. If any of these functions are overridden, they need to be updated accordingly:AuthorisationUrlGETSignInUpPOST
- Updated
SignInUprecipe interface function in thirdparty with new parameters:oAuthTokens- contains all the tokens (access_token, id_token, etc.) as returned by the providerrawUserInfoFromProvider- contains all the user profile info as returned by the provider
- Updated
AuthorisationUrlGETAPI- Changed: Doesn't accept
clientIdanymore and acceptsclientTypeinstead to determine the matching config - Added: optional
pkceCodeVerifierin the response, to support PKCE
- Changed: Doesn't accept
- Updated
SignInUpPOSTAPI- Removed:
clientId,redirectURI,authCodeResponseandcodefrom the input - Instead,
- accepts
clientTypeto determine the matching config - One of redirectURIInfo (for code flow) or oAuthTokens (for token flow) is required
- accepts
- Removed:
- Updated
AppleRedirectHandlerPOST- to accept all the form fields instead of just the code
- to use redirect URI encoded in the
stateparameter instead of using the websiteDomain config. - to use HTTP 303 instead of javascript based redirection.
- The providers array in
- Session recipe changes
- Added mandatory
tenantIdparameter to the following recipe index functions:CreateNewSessionCreateNewSessionWithoutRequestResponseValidateClaimsInJWTPayload
- Added mandatory
tenantIdin the input for the following recipe interface functions. If any of these functions are overridden, they need to be updated accordingly:CreateNewSessionGetGlobalClaimValidators
- Added
tenantIdandrevokeAcrossAllTenantsparams toRevokeAllSessionsForUserin the recipe interface. - Added
tenantIdandfetchAcrossAllTenantsparams toGetAllSessionHandlesForUserin the recipe interface. - Added
GetTenantIdfunction toTypeSessionContainer - Added
tenantIdtoFetchValuefunction inPrimitiveClaim,PrimitiveArrayClaim.
- Added mandatory
- UserRoles recipe changes
- Added mandatory
tenantIdas first parameter to the following recipe index functions:AddRoleToUserRemoveUserRoleGetRolesForUserGetUsersThatHaveRole
- Added mandatory
tenantIdin the input for the following recipe interface functions. If any of these functions are overridden, they need to be updated accordingly:AddRoleToUserRemoveUserRoleGetRolesForUserGetUsersThatHaveRole
- Added mandatory
- Similar changes in combination recipes (thirdpartyemailpassword and thirdpartypasswordless) have been made
- Even if thirdpartyemailpassword and thirdpartpasswordless recipes do not have a providers array as an input, they will still expose the third party recipe routes to the frontend.
- Returns 400 status code in emailpassword APIs if the input email or password are not of type string.
- Recipe function changes:
- Added optional
tenantIdForPasswordPolicyparam toemailpassword.UpdateEmailOrPassword,thirdpartyemailpassword.UpdateEmailOrPassword - Added optional param
tenantIdtosession.RevokeAllSessionsForUser. If tenantId is nil, sessions are revoked across all tenants - Added optional param
tenantIdtosession.getAllSessionHandlesForUser. If tenantId is nil, sessions handles across all tenants are returned
- Added optional
- Adds optional param
tenantIdtoGetUserCountwhich returns total count across all tenants if not passed. - Adds protected prop
tIdto the accessToken payload - Adds
IncludesAnyclaim validator toPrimitiveArrayClaim
-
To call any recipe function that has
tenantIdadded to it, pass'public'Before:
emailpassword.SignUp("test@example.com", "password");
After:
emailpassword.SignUp("public", "test@example.com", "password");
-
Input for provider array change as follows:
Before:
thirdparty.Google(tpmodels.GoogleConfig{ ClientID: "...", ClientSecret: "...", })
After:
tpmodels.ProviderInput{ ThirdPartyId: "google" Config: tpmodels.ProviderConfig{ Clients: []tpmodels.ProviderClientConfig{ { ClientID: "...", ClientSecret: "...", }, }, }, }
-
Single instance with multiple clients of each provider instead of multiple instances of them. Also use
clientTypeto differentiate them.clientTypepassed from the frontend will be used to determine the right config.IsDefaultoption has been removed andclientTypeis expected to be passed when there are more than one client. If there is only one client,clientTypeis optional and will be used by default.Before:
[]tpmodels.TypeProvider{ thirdparty.Google(tpmodels.GoogleConfig{ IsDefault: true, ClientID: "clientid1", ClientSecret: "...", }), thirdparty.Google(tpmodels.GoogleConfig{ ClientID: "clientid2", ClientSecret: "...", }), }
After:
[]tpmodels.ProviderInput{ { ThirdPartyId: "google", Config: tpmodels.ProviderConfig{ Clients: []tpmodels.ProviderClientConfig{ { ClientType: "web", ClientID: "clientid1", ClientSecret: "...", }, { ClientType: "android", ClientID: "clientid2", ClientSecret: "...", }, }, }, }, }
-
Change in the implementation of custom providers
- All config is part of
ProviderInput - To provide implementation for
GetProfileInfo- either use
UserInfoEndpoint,UserInfoEndpointQueryParamsandUserInfoMapto fetch the user info from the provider - or specify custom implementation in an override for
GetUserInfo(override example in the next section)
- either use
Before:
tpmodels.TypeProvider{ ID: "custom", Get: func(redirectURI, authCodeFromRequest *string, userContext supertokens.UserContext) tpmodels.TypeProviderGetResponse { return tpmodels.TypeProviderGetResponse{ AccessTokenAPI: "...", AuthorisationRedirect: tpmodels.AuthorisationRedirect{ URL: "...", Params: map[string]interface{}{}, }, GetClientId: func(userContext supertokens.UserContext) string { return "..." }, GetRedirectURI: func(userContext supertokens.UserContext) (string, error) { return "...", nil }, GetProfileInfo: func(authCodeResponse interface{}, userContext supertokens.UserContext) (tpmodels.UserInfo, error) { return tpmodels.UserInfo{ ID: "...", Email: &tpmodels.EmailStruct{ ID: "...", IsVerified: true, }, }, nil }, } }, }
After:
tpmodels.ProviderInput{ ThirdPartyID: "custom", Config: tpmodels.ProviderConfig{ Clients: []tpmodels.ProviderClientConfig{ { ClientID: "...", ClientSecret: "...", }, }, AuthorizationEndpoint: "...", AuthorizationEndpointQueryParams: map[string]interface{}{}, TokenEndpoint: "...", TokenEndpointBodyParams: map[string]interface{}{}, UserInfoEndpoint: "...", UserInfoEndpointQueryParams: map[string]interface{}{}, UserInfoMap: tpmodels.TypeUserInfoMap{ FromUserInfoAPI: tpmodels.TypeUserInfoFields{ UserId: "id", Email: "email", EmailVerified: "email_verified", }, }, }, }
Also, if the custom provider supports openid, it can automatically discover the endpoints
tpmodels.ProviderInput{ ThirdPartyID: "custom", Config: tpmodels.ProviderConfig{ Clients: []tpmodels.ProviderClientConfig{ { ClientID: "...", ClientSecret: "...", }, }, OIDCDiscoveryEndpoint: "...", UserInfoMap: tpmodels.TypeUserInfoMap{ FromUserInfoAPI: tpmodels.TypeUserInfoFields{ UserId: "id", Email: "email", EmailVerified: "emailVerified", }, }, }, }
Note: The SDK will fetch the oauth2 endpoints from the provider's OIDC discovery endpoint. No need to
/.well-known/openid-configurationto theoidcDiscoveryEndpointconfig. For eg. ifoidcDiscoveryEndpointis set to"https://accounts.google.com/", the SDK will fetch the endpoints from"https://accounts.google.com/.well-known/openid-configuration" - All config is part of
-
Any of the functions in the TypeProvider can be overridden for custom implementation
- Overrides can do the following:
- update params, headers dynamically for the authorization redirect url or in the exchange of code to tokens
- add custom logic to exchange code to tokens
- add custom logic to get the user info
tpmodels.ProviderInput{ ThirdPartyID: "custom", Config: tpmodels.ProviderConfig{ Clients: []tpmodels.ProviderClientConfig{ { ClientID: "...", ClientSecret: "...", }, }, OIDCDiscoveryEndpoint: "...", UserInfoMap: tpmodels.TypeUserInfoMap{ FromUserInfoAPI: tpmodels.TypeUserInfoFields{ UserId: "id", Email: "email", EmailVerified: "emailVerified", }, }, }, Override: func(provider *tpmodels.TypeProvider) *tpmodels.TypeProvider { oGetAuthorisationRedirectURL := provider.GetAuthorisationRedirectURL provider.GetAuthorisationRedirectURL = func(config tpmodels.ProviderConfigForClientType, redirectURIOnProviderDashboard string, userContext supertokens.UserContext) (tpmodels.TypeAuthorisationRedirect, error) { resp, err := oGetAuthorisationRedirectURL(config, redirectURIOnProviderDashboard, userContext) // your logic here return resp, err } oExchangeAuthCodeForOAuthTokens := provider.ExchangeAuthCodeForOAuthTokens provider.ExchangeAuthCodeForOAuthTokens = func(config tpmodels.ProviderConfigForClientType, code string, redirectURIOnProviderDashboard string, userContext supertokens.UserContext) (tpmodels.TypeOAuthResponse, error) { resp, err := oExchangeAuthCodeForOAuthTokens(config, code, redirectURIOnProviderDashboard, userContext) // your logic here return resp, err } oGetUserInfo := provider.GetUserInfo provider.GetUserInfo = func(config tpmodels.ProviderConfigForClientType, oAuthTokens tpmodels.TypeOAuthTokens, userContext supertokens.UserContext) (tpmodels.TypeUserInfo, error) { resp, err := oGetUserInfo(config, oAuthTokens, userContext) // your logic here return resp, err } return provider }, }
- Overrides can do the following:
-
To get access token and raw user info from the provider, override the signInUp function
thirdparty.Init(&tpmodels.TypeInput{ Override: &tpmodels.OverrideStruct{ Functions: func(originalImplementation tpmodels.RecipeInterface) tpmodels.RecipeInterface { oSignInUp := *originalImplementation.SignInUp nSignInUp := func(thirdPartyID string, thirdPartyUserID string, email string, oAuthTokens tpmodels.TypeOAuthTokens, rawUserInfoFromProvider tpmodels.TypeRawUserInfoFromProvider, tenantId string, userContext supertokens.UserContext) (tpmodels.SignInUpResponse, error) { resp, err := oSignInUp(thirdPartyID, thirdPartyUserID, email, oAuthTokens, rawUserInfoFromProvider, tenantId, userContext) // resp.OK.OAuthTokens["access_token"] // resp.OK.OAuthTokens["id_token"] // resp.OK.RawUserInfoFromProvider.FromUserInfoAPI // resp.OK.RawUserInfoFromProvider.FromIdTokenPayload return resp, err } *originalImplementation.SignInUp = nSignInUp return originalImplementation }, }, })
-
Request body of thirdparty signinup API has changed
-
If using auth code:
Before:
{ "thirdPartyId": "...", "clientId": "...", "redirectURI": "...", // optional "code": "..." }After:
{ "thirdPartyId": "...", "clientType": "...", "redirectURIInfo": { "redirectURIOnProviderDashboard": "...", // required "redirectURIQueryParams": { "code": "...", "state": "..." // ... all callback query params }, "pkceCodeVerifier": "..." // optional, use this if using PKCE flow } } -
If using tokens:
Before:
{ "thirdPartyId": "...", "clientId": "...", "redirectURI": "...", "authCodeResponse": { "access_token": "...", // required "id_token": "..." } }After:
{ "thirdPartyId": "...", "clientType": "...", "oAuthTokens": { "access_token": "...", // now optional "id_token": "..." // rest of the oAuthTokens as returned by the provider } }
-
- Compatible with Core>=6.0.0 (CDI 4.0)
- Compatible with frontend SDKs:
- supertokens-auth-react@0.34.0
- supertokens-web-js@0.7.0
- supertokens-website@17.0.2
- Fixes error handling with regenerate access token when the access token of the session is revoked.
- Fixes payload in get session when the access token version <= 2
- Fixes an issue where updating the user's password from the user management dashboard would result in a crash when using the thirdpartyemailpassword recipe (#311)
- Adds additional tests for session verification
- Now properly ignoring missing anti-csrf tokens in optional session validation
- Update email templates to fix an issue with styling on some email clients
- Fixes an issue where session verification would fail when using JWTs created by the JWT recipe (and not the session recipe)
- Fixes a bug in the session recipe where the SDK would try to fetch the JWKs from the core multiple times per minute
- Fixes bug in debug logging where line number was being printed incorrectly.
- Added a new
GetRequestFromUserContextfunction that can be used to read the original network request from the user context in overridden APIs and recipe functions
- Adds additional debug logs whenever the SDK returns a
TryRefreshTokenErrororUnauthorizedErrorto make debugging easier
- Adds additional tests for the session recipe
- Fixes #284
- Made the access token string optional in the overrideable
GetSessionfunction - Moved checking if the access token is nil into the overrideable
GetSessionfunction
- added optional password policy check in
updateEmailOrPassword
- Changed the interface and configuration of the Session recipe, see below for details. If you do not use the Session recipe directly and do not provide custom configuration, then no migration is necessary.
- Renamed
GetSessionDatatoGetSessionDataInDatabaseto clarify that it always hits the DB - Renamed
GetSessionDataWithContexttoGetSessionDataInDatabaseWithContextto clarify that it always hits the DB - Renamed
UpdateSessionDatatoUpdateSessionDataInDatabase - Renamed
UpdateSessionDataWithContexttoUpdateSessionDataInDatabaseWithContextto clarify that it always hits the DB - Renamed
SessionDatatoSessionDataInDatabaseinSessionInformation - Renamed
sessionDatatosessionDataInDatabasein the input toCreateNewSession - Added
useStaticSigningKeytoCreateJWTandCreateJWTWithContext - Added support for CDI version
2.21 - Dropped support for CDI version
2.8-2.20 GetAccessTokenPayloadwill now return standard (sub,iat,exp) claims and some SuperTokens specific claims along the user defined ones inGetAccessTokenPayload.- Some claim names are now prohibited in the root level of the access token payload
- They are:
sub,iat,exp,sessionHandle,parentRefreshTokenHash1,refreshTokenHash1,antiCsrfToken - If you used these in the root level of the access token payload, then you'll need to migrate your sessions or they will be logged out during the next refresh
- These props should be renamed (e.g., by adding a prefix) or moved inside an object in the access token payload
- You can migrate these sessions by updating their payload to match your new structure, by calling
MergeIntoAccessTokenPayload
- They are:
- New access tokens are valid JWTs now
- They can be used directly (i.e.: by calling
GetAccessTokenon the session) if you need a JWT - The
jwtprop in the access token payload is removed
- They can be used directly (i.e.: by calling
- JWT and OpenId related configuration has been removed from the Session recipe config. If necessary, they can be added by initializing the OpenId recipe before the Session recipe.
- Changed the Session recipe interface - CreateNewSession, GetSession and RefreshSession overrides now do not take response and request and return status instead of throwing
- Renamed
AccessTokenPayloadtoCustomClaimsInAccessTokenPayloadinSessionInformation(the return value ofGetSessionInformation). This reflects the fact that it doesn't contain some default claims (sub,iat, etc.)
- Refactors the URL for the JWKS endpoint exposed by SuperTokens core
- Added new optional
useStaticSigningKeyparam toCreateJWT - The Session recipe now always initializes the OpenID recipe if it hasn't been initialized.
- Refactored how access token validation is done
- Added support for new access token version
- Removed the handshake call to improve start-up times
- Removed
GetAccessTokenLifeTimeMSandGetRefreshTokenLifeTimeMSfunctions - Added
ExposeAccessTokenToFrontendInCookieBasedAuth(defaults tofalse) option to the Session recipe config - Added new
checkDatabaseparam toVerifySessionandGetSession - Removed deprecated
UpdateAccessTokenPayload,UpdateAccessTokenPayloadWithContext,RegenerateAccessTokenandRegenerateAccessTokenWithContextfrom the Session recipe interface - Added
CreateNewSessionWithoutRequestResponse,CreateNewSessionWithContextWithoutRequestResponse,GetSessionWithoutRequestResponse,GetSessionWithContextWithoutRequestResponse,RefreshSession,RefreshSessionWithContextWithoutRequestResponseto the Session recipe. - Added
GetAllSessionTokensDangerouslyto session objects (SessionContainer) - Added
AttachToRequestResponseto session objects (SessionContainer)
- You need to update the core version
- There are manual migration steps needed. Check out the core changelogs for more details.
- Add
ExposeAccessTokenToFrontendInCookieBasedAuth: trueto the Session recipe config on the backend if you need to access the JWT on the frontend. - Choose a prop from the following list. We'll use
subin the code below, but you can replace it with another from the list if you used it in a custom access token payload.subiatexpsessionHandle
- On the frontend where you accessed the JWT before by:
(await Session.getAccessTokenPayloadSecurely()).jwtupdate to:
let jwt = null;
const accessTokenPayload = await Session.getAccessTokenPayloadSecurely();
if (accessTokenPayload.sub !== undefined) {
jwt = await Session.getAccessToken();
} else {
// This branch is only required if there are valid access tokens created before the update
// It can be removed after the validity period ends
jwt = accessTokenPayload.jwt;
}- On the backend if you accessed the JWT before by
session.GetAccessTokenPayload()["jwt"]please update to:
var jwt string
accessTokenPayload := session.GetAccessTokenPayload();
if (accessTokenPayload["sub"] != nil) {
jwt = session.GetAccessToken();
} else {
// This branch is only required if there are valid access tokens created before the update
// It can be removed after the validity period ends
jwt = accessTokenPayload["jwt"].(string);
}- You can add an issuer claim to access tokens by overriding the
CreateNewSessionfunction in the session recipe init.- Check out https://supertokens.com/docs/passwordless/common-customizations/sessions/claims/access-token-payload#during-session-creation for more information
- You can add an issuer claim to JWTs created by the JWT recipe by passing the
issclaim as part of the payload. - You can set the OpenId discovery configuration as follows:
Before:
func main() {
supertokens.Init(supertokens.TypeInput{
RecipeList: []supertokens.Recipe{
session.Init(&sessmodels.TypeInput{
Jwt: &sessmodels.JWTInputConfig{
Enable: true,
Issuer: "...",
},
}),
},
})
}After:
func main() {
supertokens.Init(supertokens.TypeInput{
RecipeList: []supertokens.Recipe{
session.Init(&sessmodels.TypeInput{
GetTokenTransferMethod: func(req *http.Request, forCreateNewSession bool, userContext supertokens.UserContext) sessmodels.TokenTransferMethod {
return sessmodels.HeaderTransferMethod
},
Override: &sessmodels.OverrideStruct{
OpenIdFeature: &openidmodels.OverrideStruct{
Functions: func(originalImplementation openidmodels.RecipeInterface) openidmodels.RecipeInterface {
(*originalImplementation.GetOpenIdDiscoveryConfiguration) = func(userContext *map[string]interface{}) (openidmodels.GetOpenIdDiscoveryConfigurationResponse, error) {
return openidmodels.GetOpenIdDiscoveryConfigurationResponse{
OK: &struct{Issuer string; Jwks_uri string}{
Issuer: "your issuer",
Jwks_uri: "https://your.api.domain/auth/jwt/jwks.json",
},
}, nil
}
return originalImplementation
},
},
},
}),
},
})
}Related functions/prop names have changes (sessionData became sessionDataFromDatabase):
- Renamed
GetSessionDatatoGetSessionDataFromDatabaseto clarify that it always hits the DB - Renamed
UpdateSessionDatatoUpdateSessionDataInDatabase - Renamed
sessionDatatosessionDataInDatabaseinSessionInformationand the input toCreateNewSession
- You should now set
CheckDatabaseto true in the verifySession params.
- You should now set
useDynamicAccessTokenSigningKeyin the Session recipe config.
- Update you application logic to rename those props (e.g., by adding a prefix)
- Update the session recipe config (in this example
subis the protected property we are updating by adding theappprefix):
Before:
func main() {
supertokens.Init(supertokens.TypeInput{
RecipeList: []supertokens.Recipe{
session.Init(&sessmodels.TypeInput{
Override: &sessmodels.OverrideStruct{
Functions: func(originalImplementation sessmodels.RecipeInterface) sessmodels.RecipeInterface {
originalCreateNewSession := *originalImplementation.CreateNewSession
(*originalImplementation.CreateNewSession) = func(req *http.Request, res http.ResponseWriter, userID string, accessTokenPayload, sessionData map[string]interface{}, userContext supertokens.UserContext) (sessmodels.SessionContainer, error) {
if accessTokenPayload == nil {
accessTokenPayload = map[string]interface{}{}
}
accessTokenPayload["sub"] = userID + "!!!"
return originalCreateNewSession(req, res, userID, accessTokenPayload, sessionData, userContext)
}
return originalImplementation
},
},
}),
},
})
}After:
func main() {
supertokens.Init(supertokens.TypeInput{
RecipeList: []supertokens.Recipe{
session.Init(&sessmodels.TypeInput{
Override: &sessmodels.OverrideStruct{
Functions: func(originalImplementation sessmodels.RecipeInterface) sessmodels.RecipeInterface {
originalGetSession := *originalImplementation.GetSession
(*originalImplementation.GetSession) = func(req *http.Request, res http.ResponseWriter, options *sessmodels.VerifySessionOptions, userContext *map[string]interface{}) (*sessmodels.TypeSessionContainer, error) {
result, err := originalGetSession(req, res, options, userContext)
if result != nil {
originalPayload := result.GetAccessTokenPayload()
if originalPayload["appSub"] == nil {
result.MergeIntoAccessTokenPayload(map[string]interface{}{
"appSub": originalPayload["sub"],
"sub": nil,
})
}
}
return result, err
}
originalCreateNewSession := *originalImplementation.CreateNewSession
(*originalImplementation.CreateNewSession) = func(req *http.Request, res http.ResponseWriter, userID string, accessTokenPayload map[string]interface{}, sessionData map[string]interface{}, userContext *map[string]interface{}) (*sessmodels.TypeSessionContainer, error) {
if accessTokenPayload == nil {
accessTokenPayload = map[string]interface{}{}
}
accessTokenPayload["sub"] = userID + "!!!"
return originalCreateNewSession(req, res, userID, accessTokenPayload, sessionData, userContext)
}
return originalImplementation
},
},
}),
},
})
}This example uses GetSession, but the changes required for the other ones are very similar. Before:
session.Init(&sessmodels.TypeInput{
Override: &sessmodels.OverrideStruct{
Functions: func(originalImplementation sessmodels.RecipeInterface) sessmodels.RecipeInterface {
originalGetSession := *originalImplementation.GetSession
newGetSession := func(req *http.Request, res http.ResponseWriter, options *sessmodels.VerifySessionOptions, userContext supertokens.UserContext) (sessmodels.SessionContainer, error) {
response, err := originalGetSession(req, res, options, userContext)
if err != nil {
return nil, err
}
return response, nil
}
*originalImplementation.GetSession = newGetSession
return originalImplementation
},
},
})After:
session.Init(&sessmodels.TypeInput{
Override: &sessmodels.OverrideStruct{
Functions: func(originalImplementation sessmodels.RecipeInterface) sessmodels.RecipeInterface {
originalGetSession := *originalImplementation.GetSession
newGetSession := func(accessToken string, antiCSRFToken *string, options *sessmodels.VerifySessionOptions, userContext supertokens.UserContext) (sessmodels.SessionContainer, error) {
defaultUserContext := (*userContext)["_default"].(map[string]interface{})
request := defaultUserContext["request"]
print(request)
response, err := originalGetSession(accessToken, antiCSRFToken, options, userContext)
if err != nil {
return nil, err
}
return response, nil
}
*originalImplementation.GetSession = newGetSession
return originalImplementation
},
},
}),- Added missing arguments in
GetUsersNewestFirstandGetUsersOldestFirst
- Email template for verify email updated
- Changed email template to render correctly in outlook
- Fixes panic issue in input validation for emailpassword APIs - #254
- Adds search APIs to the dashboard recipe
- Adds a telemetry API to the dashboard recipe
- Adds unit test for Apple callback form post
- Updates all example apps to also initialise dashboard recipe
- Adds login with gitlab (for single tenant only) and bitbucket
- Adds APIs and logic to the dashboard recipe to enable email password based login
- Email template updates
- Fixes issue with go-fiber example, where updating accessTokenPayload from user defined endpoint doesn't reflect in the response cookies.
- Added support for authorizing requests using the
Authorizationheader instead of cookies - Optional
GetTokenTransferMethodconfig is Session recipe input, which determines the token transfer method. - Check out https://supertokens.com/docs/thirdpartyemailpassword/common-customizations/sessions/token-transfer-method for more information
- ID Refresh token is removed from the SDK
- The frontend SDK should be updated to a version supporting the header-based sessions!
- supertokens-auth-react: >= 0.31.0
- supertokens-web-js: >= 0.5.0
- supertokens-website: >= 16.0.0
- supertokens-react-native: >= 4.0.0
- supertokens-ios >= 0.2.0
- supertokens-android >= 0.3.0
- supertokens-flutter >= 0.1.0
CreateNewSessionnow requires passing the request as well as the response.- This only requires a change if you manually created sessions (e.g.: during testing)
- Check the migration example below
CreateNewSessionWithContextandCreateNewSessionin the session recipe accepts new- Only supporting FDI 1.16
parameter
reqof type*http.Request
Before:
func httpHandler(w http.ResponseWriter, r *http.Request,) {
sessionContainer, err := session.CreateNewSession(w, "userId", map[string]interface{}{}, map[string]interface{}{})
if err != nil {
// handle error
}
// ...
}After:
func httpHandler(w http.ResponseWriter, r *http.Request,) {
sessionContainer, err := session.CreateNewSession(r, w, "userId", map[string]interface{}{}, map[string]interface{}{})
if err != nil {
// handle error
}
// ...
}- Fixes an issue in the dashboard recipe when fetching user details for passwordless users that don't have an email associated with their accounts
- Updates dashboard version
- Updates user GET API for dashboard recipe
- Adds optional
UsernametoSMTPSettings, which can be used for SMTP login if username is different fromFrom.Email.
- Fixes
newPasswordvalidation in Dashboard API
- Fixes panic issue with dashboard usersGet API
- Fixes issue where if SendEmail is overridden with a different email, it will reset that email.
- Adds APIs for user details to the dashboard recipe
- Updates dashboard version to 0.2
- Fixes go fiber to handle handler chaining correctly with verifySession.
- Added test to check JWT contains updated value when MergeIntoAccessTokenPayload is called.
- Adds updating of session claims in email verification token generation API in case the session claims are outdated.
- Updated Frontend integration test server for angular tests
- Fixes Apple secret key computation
- Updated google token endpoint
- Fixes crash in findRightProvider
- Removed default defaultMaxAge from session claim base classes
- Added a 5 minute defaultMaxAge to UserRoleClaim, PermissionClaim and EmailVerificationClaim
- Using UnixNano instead of UnixMilli to support go version < 1.17
- Clears cookies before calling onUnauthorizedError handler if ClearCookies is nil or set to true
- Email verification endpoints will now clear the session if called by a deleted/unknown user
- Email verification endpoints will now clear the session if called by a deleted/unknown user
- Adds Dashboard recipe
- Added support for session claims with related interfaces and classes.
- Added
EmailVerificationClaim. Modeconfig is added toevmodels.TypeInputGetEmailForUserIDconfig is added toevmodels.TypeInput- Added
OnInvalidClaimoptional error handler to send InvalidClaim error responses. - Added
INVALID_CLAIMStoSessionErrors. - Added
InvalidClaimStatusCodeoptional config to set the status code of InvalidClaim errors. - Added
OverrideGlobalClaimValidatorsto options ofgetSessionandverifySession. - Added
MergeIntoAccessTokenPayloadto the Session recipe and session objects which should be preferred to the now deprecatedUpdateAccessTokenPayload. - Added
AssertClaims,ValidateClaimsForSessionHandle,ValidateClaimsInJWTPayloadto the Session recipe to support validation of the newly addedEmailVerificationClaim. - Added
FetchAndSetClaim,GetClaimValue,SetClaimValueandRemoveClaimto the Session recipe to manage claims. - Added
AssertClaims,FetchAndSetClaim,GetClaimValue,SetClaimValueandRemoveClaimto session objects to manage claims. - Added sessionContainer to the input of
GenerateEmailVerifyTokenPOST,VerifyEmailPOST,IsEmailVerifiedGET. - Adds default UserContext for verifySession calls that contains the request object.
- Added
UserRoleClaimandPermissionClaimto user roles recipe.
- Removes support for FDI < 1.15
- Changed
SignInUpthird party recipe function to accept an email string instead of an object that takes{ID: string, IsVerified: boolean}. - The frontend SDK should be updated to a version supporting session claims!
- supertokens-auth-react: >= 0.25.0
- supertokens-web-js: >= 0.2.0
EmailVerificationrecipe is now not initialized as part of auth recipes, it should be added to therecipeListdirectly instead.- Email verification related overrides (
EmailVerificationFeatureprop ofOverride) moved from auth recipes into theEmailVerificationrecipe config. - ThirdParty recipe no longer takes EmailDelivery config -> use Emailverification recipe's EmailDelivery instead.
- Moved email verification related configs from the
EmailDeliveryconfig of auth recipes into a separateEmailVerificationemail delivery config. - Updated return type of
GetEmailForUserIdin theEmailVerificationrecipe config. It should now returnOK,EmailDoesNotExistErrororUnknownUserIDErroras response. - Removed
GetResetPasswordURL,GetEmailVerificationURL,GetLinkDomainAndPath. Changing these urls can be done in the email delivery configs instead. - Removed
UnverifyEmail,RevokeEmailVerificationTokens,IsEmailVerified,VerifyEmailUsingTokenandCreateEmailVerificationTokenfrom auth recipes. These should be called on theEmailVerificationrecipe instead. - Changed function signature for email verification APIs to accept a sessionContainer as an input.
- Changed Session API interface functions:
RefreshPOSTnow returns a Session container object.SignOutPOSTnow takes in an optional session object as a parameter.
SessionContaineris renamed toTypeSessionContainerandSessionContaineris now an alias for*TypeSessionContainer. All*SessionContaineris now replaced withSessionContainer.- Removed unused parameter
emailfromthirdpartyemailpassword.GetUserByThirdPartyInfoWithContextfunction.
Before:
supertokens.Init(supertokens.TypeInput{
AppInfo: supertokens.AppInfo{
AppName: "...",
APIDomain: "...",
WebsiteDomain: "...",
},
RecipeList: []supertokens.Recipe{
emailpassword.Init(&epmodels.TypeInput{
EmailVerificationFeature: evmodels.TypeInput{
// ...
},
Override: &epmodels.OverrideStruct{
EmailVerificationFeature: &evmodels.OverrideStruct{
// ...
},
},
}),
},
})After the update:
supertokens.Init(supertokens.TypeInput{
AppInfo: supertokens.AppInfo{
AppName: "...",
APIDomain: "...",
WebsiteDomain: "...",
},
RecipeList: []supertokens.Recipe{
emailverification.Init(evmodels.TypeInput{
Mode: evmodels.ModeRequired, // or evmodels.ModeOptional
// all config should be moved here from the emailVerificationFeature prop of the EmailPassword recipe config
Override: &evmodels.OverrideStruct{
// move the overrides from the emailVerificationFeature prop of the override config in the EmailPassword init here
},
}),
emailpassword.Init(nil),
},
})If you turn on email verification your email-based passwordless users may be redirected to an email verification screen in their existing session. Logging out and logging in again will solve this problem or they could click the link in the email to verify themselves.
You can avoid this by running a script that will:
- list all users of passwordless
- create an emailverification token for each of them if they have email addresses
- user the token to verify their address
Something similar to this script:
package main
import (
"github.com/supertokens/supertokens-golang/recipe/emailverification"
"github.com/supertokens/supertokens-golang/recipe/emailverification/evmodels"
"github.com/supertokens/supertokens-golang/recipe/passwordless"
"github.com/supertokens/supertokens-golang/recipe/passwordless/plessmodels"
"github.com/supertokens/supertokens-golang/recipe/session"
"github.com/supertokens/supertokens-golang/supertokens"
)
func main() {
supertokens.Init(supertokens.TypeInput{
AppInfo: supertokens.AppInfo{
AppName: "...",
APIDomain: "...",
WebsiteDomain: "...",
},
RecipeList: []supertokens.Recipe{
emailverification.Init(evmodels.TypeInput{
Mode: evmodels.ModeRequired,
}),
passwordless.Init(plessmodels.TypeInput{
FlowType: "USER_INPUT_CODE_AND_MAGIC_LINK",
ContactMethodEmailOrPhone: plessmodels.ContactMethodEmailOrPhoneConfig{
Enabled: true,
},
}),
session.Init(nil),
},
})
var paginationToken *string
recipeList := []string{"passwordless"}
limit := 100
done := false
for !done {
userList, err := supertokens.GetUsersNewestFirst(paginationToken, &limit, &recipeList)
if err != nil {
panic(err)
}
for _, user := range userList.Users {
if user.RecipeId == "passwordless" && user.User["email"] != nil {
token, err := emailverification.CreateEmailVerificationToken(user.User["id"].(string), nil)
if err != nil {
panic(err)
}
if token.OK != nil {
_, err := emailverification.VerifyEmailUsingToken(token.OK.Token)
if err != nil {
panic(err)
}
}
}
done = (userList.NextPaginationToken == nil)
paginationToken = userList.NextPaginationToken
}
}
}The UserRoles recipe now adds role and permission information into the access token payload by default. If you are already doing this manually, this will result in duplicate data in the access token.
- You can disable this behaviour by setting
SkipAddingRolesToAccessTokenandSkipAddingPermissionsToAccessTokento true in the recipe init. - Check how to use the new claims in the updated guide: https://supertokens.com/docs/userroles/protecting-routes
- Adds test to verify that session container uses overridden functions
- Adds with-go-zero example: #157
- UserId Mapping functionality and compatibility with CDI 2.15
- Adds
CreateUserIdMapping,GetUserIdMapping,DeleteUserIdMapping,UpdateOrDeleteUserIdMappingInfofunctions to supertokens package
- Fixes JWKS Keyfunc call that resulted in a goroutine leak: #155
- Fixes issue with 404 status being sent for apple redirect callback route.
- Changes session recipe interfaces to not return an
UNAUTHORISEDerror when the input is a sessionHandle: https://github.com/supertokens/backend/issues/83 GetSessionInformationnow returnsnilis the session does not existUpdateSessionDatanow returnsnilif the inputsessionHandledoes not exist.UpdateAccessTokenPayloadnow returnsfalseif the inputsessionHandledoes not exist.RegenerateAccessTokennow returnsnilif the input access token'ssessionHandledoes not exist.- The session container functions have not changed in behaviour and return errors if
sessionHandledoes not exist. This works on the current session.
- Clears cookies when RevokeSession is called using the session container, even if the session did not exist from before: supertokens/supertokens-node#343
- Adds default userContext for API calls that contains the request object. It can be used in APIs / functions override like so:
SignIn: func (..., userContext supertokens.UserContext) {
if _default, ok := (*userContext)["_default"].(map[string]interface{}); ok {
if req, ok := _default["request"].(*http.Request); ok {
// do something here with the request object
}
}
}- Adds unit tests for resend email & sms services for passwordless and thirdpartypasswordless recipes
- Adds User Roles recipe and compatibility with CDI 2.14
- Fixes panic while returning empty result object with nil error in the API overrides. Related to #107
- Renamed
SMTPServiceConfigtoSMTPSettings - Changed type of
SecureinSMTPSettingsfrom*booltobool - Renamed
SMTPServiceFromConfigtoSMTPFrom - Renamed
SMTPGetContentResulttoEmailContent - Renamed
SMTPTypeInputtoSMTPServiceConfig - Renamed field
SMTPSettingstoSettingsinSMTPServiceConfig - Renamed
SMTPServiceInterfacetoSMTPInterface - Renamed all instances of
MakeSmtpServicetoMakeSMTPService - All instances of
MakeSMTPServicereturns*EmailDeliveryInterfaceinstead ofEmailDeliveryInterface - Renamed
TwilioServiceConfigtoTwilioSettings - Renamed
TwilioGetContentResulttoSMSContent - Renamed
TwilioTypeInputtoTwilioServiceConfig - Renamed field
TwilioSettingstoSettingsinTwilioServiceConfig - Changed types of fields
FromandMessagingServiceSidinTwilioSettingsfrom*stringtostring - Renamed
MakeSupertokensServicetoMakeSupertokensSMSService - All instances of
MakeSupertokensSMSServiceandMakeTwilioServicereturns*SmsDeliveryInterfaceinstead ofSmsDeliveryInterface - Removed
SupertokensServiceConfigandMakeSupertokensSMSServiceacceptsapiKeydirectly instead ofSupertokensServiceConfig - Renamed
TwilioServiceInterfacetoTwilioInterface - Removes support for FDIs that are < 1.14
- Exposed
MakeSMTPServicefrom emailverification, emailpassword, passwordless, thirdparty, thirdpartyemailpassword and thirdpartypasswordless recipes - Exposed
MakeSupertokensSMSServiceandMakeTwilioServicefrom passwordless and thirdpartypasswordless recipes
- Fixes Cookie SameSite config validation.
- Changes
getEmailForUserIdForEmailVerificationfunction inside thirdpartypasswordless to take into account passwordless emails and return an empty string in case a passwordless email doesn't exist. This helps situations where the dev wants to customise the email verification functions in the thirdpartypasswordless recipe.
EmailDeliveryuser config for Emailpassword, Thirdparty, ThirdpartyEmailpassword, Passwordless and ThirdpartyPasswordless recipes.SmsDeliveryuser config for Passwordless and ThirdpartyPasswordless recipes.Twilioservice integration for SmsDelivery ingredient.SMTPservice integration for EmailDelivery ingredient.Supertokensservice integration for SmsDelivery ingredient.
- For Emailpassword recipe input config,
ResetPasswordUsingTokenFeature.CreateAndSendCustomEmailandEmailVerificationFeature.CreateAndSendCustomEmailhave been deprecated. - For Thirdparty recipe input config,
EmailVerificationFeature.CreateAndSendCustomEmailhas been deprecated. - For ThirdpartyEmailpassword recipe input config,
ResetPasswordUsingTokenFeature.CreateAndSendCustomEmailandEmailVerificationFeature.CreateAndSendCustomEmailhave been deprecated. - For Passwordless recipe input config,
CreateAndSendCustomEmailandCreateAndSendCustomTextMessagehave been deprecated. - For ThirdpartyPasswordless recipe input config,
CreateAndSendCustomEmail,CreateAndSendCustomTextMessageandEmailVerificationFeature.CreateAndSendCustomEmailhave been deprecated.
Following is an example of ThirdpartyPasswordless recipe migration. If your existing code looks like
func passwordlessLoginEmail(email string, userInputCode *string, urlWithLinkCode *string, codeLifetime uint64, preAuthSessionId string, userContext supertokens.UserContext) error {
// some custom logic
}
func passwordlessLoginSms(phoneNumber string, userInputCode *string, urlWithLinkCode *string, codeLifetime uint64, preAuthSessionId string, userContext supertokens.UserContext) error {
// some custom logic
}
func verifyEmail(user tplmodels.User, emailVerificationURLWithToken string, userContext supertokens.UserContext) {
// some custom logic
}
supertokens.Init(supertokens.TypeInput{
AppInfo: supertokens.AppInfo{
AppName: "...",
APIDomain: "...",
WebsiteDomain: "...",
},
RecipeList: []supertokens.Recipe{
thirdpartypasswordless.Init(tplmodels.TypeInput{
FlowType: "...",
ContactMethodEmailOrPhone: plessmodels.ContactMethodEmailOrPhoneConfig{
Enabled: true,
CreateAndSendCustomEmail: passwordlessLoginEmail,
CreateAndSendCustomTextMessage: passwordlessLoginSms,
},
EmailVerificationFeature: &tplmodels.TypeInputEmailVerificationFeature{
CreateAndSendCustomEmail: verifyEmail,
},
}),
},
})After migration to using new EmailDelivery and SmsDelivery config, your code would look like:
func passwordlessLoginEmail(email string, userInputCode *string, urlWithLinkCode *string, codeLifetime uint64, preAuthSessionId string, userContext supertokens.UserContext) error {
// some custom logic
return nil
}
func passwordlessLoginSms(phoneNumber string, userInputCode *string, urlWithLinkCode *string, codeLifetime uint64, preAuthSessionId string, userContext supertokens.UserContext) error {
// some custom logic
return nil
}
func verifyEmail(user tplmodels.User, emailVerificationURLWithToken string, userContext supertokens.UserContext) {
// some custom logic
}
var sendEmail = func(input emaildelivery.EmailType, userContext supertokens.UserContext) error {
if input.EmailVerification != nil {
verifyEmail(tplmodels.User{ID: input.EmailVerification.User.ID, Email: &input.EmailVerification.User.Email}, input.EmailVerification.EmailVerifyLink, userContext)
} else if input.PasswordlessLogin != nil {
return passwordlessLoginEmail(input.PasswordlessLogin.Email, input.PasswordlessLogin.UserInputCode, input.PasswordlessLogin.UrlWithLinkCode, input.PasswordlessLogin.CodeLifetime, input.PasswordlessLogin.PreAuthSessionId, userContext)
}
return nil
}
var sendSms = func(input smsdelivery.SmsType, userContext supertokens.UserContext) error {
if input.PasswordlessLogin != nil {
return passwordlessLoginSms(input.PasswordlessLogin.PhoneNumber, input.PasswordlessLogin.UserInputCode, input.PasswordlessLogin.UrlWithLinkCode, input.PasswordlessLogin.CodeLifetime, input.PasswordlessLogin.PreAuthSessionId, userContext)
}
return nil
}
supertokens.Init(supertokens.TypeInput{
AppInfo: supertokens.AppInfo{
AppName: "...",
APIDomain: "...",
WebsiteDomain: "...",
},
RecipeList: []supertokens.Recipe{
thirdpartypasswordless.Init(tplmodels.TypeInput{
FlowType: "...",
ContactMethodEmailOrPhone: plessmodels.ContactMethodEmailOrPhoneConfig{
Enabled: true,
},
EmailDelivery: &emaildelivery.TypeInput{
Service: &emaildelivery.EmailDeliveryInterface{
SendEmail: &sendEmail,
},
},
SmsDelivery: &smsdelivery.TypeInput{
Service: &smsdelivery.SmsDeliveryInterface{
SendSms: &sendSms,
},
},
}),
},
})- Fixes panic when call to thirdparty provider API returns a non 2xx status.
- supertokens/supertokens-node#220
- Adds
{status: "GENERAL_ERROR", message: string}as a possible output to all the APIs. - Changes
FIELD_ERRORoutput status in third party recipe API to beGENERAL_ERROR. - Replaced
FIELD_ERRORstatus type in third party signinup API withGENERAL_ERROR. - Removed
FIELD_ERRORstatus type from third party signinup recipe function.
- Adds
- Changes output of
VerifyEmailPOSTtoVerifyEmailPOSTResponse - Changes output of
PasswordResetPOSTtoResetPasswordPOSTResponse SignInUprecipe function doesn't returnFIELD_ERRORanymore in thirdparty, thirdpartypasswordless and thirdpartyemailpassword recipe.SignInUpPOSTapi function returnsGENERAL_ERRORinstead ofFIELD_ERRORin thirdparty, thirdpartypasswordless and thirdpartyemailpassword recipe.- If there is an error in sending SMS or email in passwordless based recipes, then we no longer return a GENERAL_ERROR, but instead, we return a regular golang error.
- Changes
GetJWKSGETin JWT recipe to returnGetJWKSAPIResponse(that also contains a General Error response) - Changes
GetOpenIdDiscoveryConfigurationGETin Open ID recipe to returnGetOpenIdDiscoveryConfigurationAPIResponse(that also contains a General Error response) - Renames
OnGeneralErrorcallback (that's in user input) toOnSuperTokensAPIError - If there is an error in the
errorHandler, we no longer callOnSuperTokensAPIErrorin that, but instead, we return an error back.
- Fixes facebook login
- Fixes issue in reading request body in API override: #116
- Fixes issue in writing custom response in API override with general error
- Adds unit tests to thirdpartypasswordless recipe
- Fixes the function signature of the
GetUserByThirdPartyInfofunction in thethirdpartypasswordlessrecipe.
- Fixes issue in writing custom response in API Override
- #102. Sending
preAuthSessionIDinstead ofpreAuthSessionIdto the core. - Fixes the error message in AuthorizationUrlAPI function in the
apimodule of the thirdparty recipe in case when providers is nil
- Adds both with context and without context functions to thirdparty passwordless recipe, Like all other recipes. Where we expose both WithContext functions and without context functions, which are basically the same as WithContext ones with an emtpy map[string]interface{} passed as context
- Adds unit tests to passwordless recipe
- Fixes existing action to run go mod tidy in the examples folder
- Fixes stopSt function in testing utils
- Fixes bug in the revokeCode function of the recipeimplementation in passwordless recipe
- Adds Github Actions for testing and pre-commit hooks.
- Adds more unit tests for thirdpary email password recipe
- Adds test to jwt recipe
- Adds test to opendID recipe
- Third party sign in up API response correction.
- Adds functions to delete passwordless user info in recipes that have passwordless users.
- Fixes bug in signinup helper function exposed by passwordless recipe
- Adds UserMetadata recipe
- Adds functions for debug logging
- workflow to enforce go mod tidy is run when issuing a PR.
- Checks if discord returned email before setting it in the profile info obj.
- Adds thirdpartypasswordless recipe: supertokens/supertokens-core#331
- Adds testing framework along with unit tests for the recipes
- Adds unit tests for thirdparty recipe and thirdpartyemailpassword recipe
- Adds example implementation with go fiber
- Adds user context to all functions exposed to the user, and to API and Recipe interface functions. This is a non breaking change for User exposed function calls, but a breaking change if you are using the Recipe or APIs override feature
- Returns session from API interface functions that create a session
- Renames functions in ThirdPartyEmailPassword recipe (supertokens/supertokens-node#219):
- Recipe Interface:
SignInUp->ThirdPartySignInUpSignUp->EmailPasswordSignUpSignIn->EmailPasswordSignIn
- API Interface:
EmailExistsGET->EmailPasswordEmailExistsGET
- User exposed functions (in
recipe/thirdpartyemailpassword/main.go)SignInUp->ThirdPartySignInUpSignUp->EmailPasswordSignUpSignIn->EmailPasswordSignIn
- Recipe Interface:
- Uses recipe interface inside session class so that any modification to those get reflected in the session class functions too.
- Adds ability to give a path for each of the hostnames in the connectionURI: supertokens/supertokens-node#252
- Adds workflow to verify if pr title follows conventional commits
- Added userId as an optional property to the response of
recipe/user/password/reset(Compatibility with CDI 2.12).
- Added
regenerateAccessTokenas a new recipe function for the session recipe. - Added a bunch of new functions inside the session container which gives user the ability to either call a function with userContext or just call the function without it (for example:
RevokeSessionandRevokeSessionWithContext)
- Allows passing of custom user context everywhere: #64
- Fixes supertokens/supertokens-node#244 - throws an error if a user tries to update email / password of a third party login user.
- Adds check to see if user has provided empty connectionInfo
- Adds fixes to solve casting of data in session-functions
- Adds passwordless recipe
- Adds compatibility with FDI 1.11 and CDI 2.11
- Fixes issue of methods getting hidden due to DoneWriter wrapper around ResponseWriter: #55
- Sends application/json content-type in
SendNon200Responsefunction: #53
- Add DeleteUser function
- The ability to enable JWT creation with session management, this allows easier integration with services that require JWT based authentication: supertokens/supertokens-core#250
- Upgrade
keyfuncdependency to stable version.
- Removes use of apiGatewayPath from apple's redirect URI since that is already there in the apiBasePath
- Changes
FIELD_ERRORtype in sign in up response fromErrortoErrorMsg
- Sign in with google workspaces and discord
- If getting profile info from third party provider throws an error, that is propagated a
FIELD_ERRORto the client.
- Does not send a response if the user has already sent the response: supertokens/supertokens-node#197
- When routing, ignores
ridvalue"anti-csrf": supertokens/supertokens-node#202
- Makes recipe and API interface have pointers to functions to fix supertokens/supertokens-node#199
- Support for FDI 1.10:
- Allow thirdparty
/signinup POSTAPI to takeauthCodeResponseXORcodeso that it can supprt OAuth via PKCE
- Allow thirdparty
- Makes recipe and API interface have pointers to functions to fix supertokens/supertokens-node#199
- Support for FDI 1.10:
- Adds apple sign in callback API
- Optional
getRedirectURIfunction added to social providers in case we set theredirect_urion the backend. - Adds optional
IsDefaultparam to auth providers so that they can be reused with different credentials. - Adds sign in with apple support: #20
- Removes
SignInUpPostfrom thirdpartyemailpassword API interface and replaces it with three APIs:EmailPasswordSignInPOST,EmailPasswordSignUpPOSTandThirdPartySignInUpPOST: supertokens/supertokens-node#192 - Renames all JWT function names to use AccessToken instead for clarity
- Changes implementation such that actual client IDs are not in the SDK, removes imports for OAuth dev related code.
- URL protocol is being taken into account when determining the value of cookie same site: #36
- Adds OAuth development keys for Google and Github for faster recipe implementation.
- Support for FDI 1.9
- JWT Recipe
- Sets response content-type as JSON
- Support for multiple access token signing keys: supertokens/supertokens-core#305
- Supporting CDI 2.9
- Initial version of the repo