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.
- Adds support for normalizing the connection URI's before returning them in dashboard GET response.
- Removes use of
UserContext
in user GET API in dashboard recipe. - Makes optional fields properly optional (i:e the value can be omitted entirely)
- Changes the type of
value
inTypeFormField
tointerface{}
instead ofstring
to 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
websiteDomain
andapiDomain
to core for telemetry. boxyURL
is no more mandatory input inadditionalConfig
while adding boxy-saml provider in thirdParty.- Adds
JWKSRefreshIntervalSec
config tosessmodels.TypeInput
to set the default JWKS cache duration. The default is 4 hours.
- Removes the default
maxAgeInSeconds
value (previously 300 seconds) in EmailVerification Claim. If the claim value is true andmaxAgeInSeconds
is not provided, it will not be refetched. - Updates
NetworkInterceptor
function signature to returnerror
along with*http.Request
- SDK will no longer add
.well-known/openid-configuration
to theOIDCDiscoveryEndpoint
config in thirdParty providers. If you have specified any customOIDCDiscoveryEndpoint
in 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
IncludeInNonPublicTenantsByDefault
for 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/v2
You should see the thirdParty providers in the response using
response.thirdParty.providers
-
For each config in providers list, if you have
oidcDiscoveryEndpoint
in the config, update it to include.well-known/openid-configuration
at 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)
}
}
}
}
refreshPOST
andRefreshSession
now 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
rid
query param from:- email verification links
- passwordless magic links
- password reset links
- If
rid
header 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 therid
header). - New API handlers are:
GET /emailpassword/email/exists
=> email password, does email exist API (used to beGET /signup/email/exists
withrid
ofemailpassword
orthirdpartyemailpassword
which is now deprecated)GET /passwordless/email/exists
=> email password, does email exist API (used to beGET /signup/email/exists
withrid
ofpasswordless
orthirdpartypasswordless
which is now deprecated)GET /passwordless/phonenumber/exists
=> email password, does email exist API (used to beGET /signup/phonenumber/exists
which is now deprecated)
- Support for FDI 2.0
-
If you were using
ThirdPartyEmailPassword
, you should now initThirdParty
andEmailPassword
recipes 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 initThirdParty
andPasswordless
recipes 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", "[email protected]")
After:
thirdPartyUserInfo, err := thirdparty.GetUsersByEmail("public", "[email protected]") if err != nil { // TODO: Handle error } emailPasswordUserInfo, err := emailpassword.GetUserByEmail("public", "[email protected]") 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", "[email protected]")
After:
thirdPartyUserInfo, err := thirdparty.GetUsersByEmail("public", "[email protected]") if err != nil { return } passwordlessUserInfo, err := passwordless.GetUserByEmail("public", "[email protected]") 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
OlderCookieDomain
config option in the session recipe. This will allow users to clear cookies from the older domain when theCookieDomain
is changed. - If
VerifySession
detects 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/refresh
by default) API changes:- now returns 500 error if multiple access tokens are present in the request and
config.OlderCookieDomain
is 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
OlderCookieDomain
is 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
normaliseSessionScopeOrThrowError
util 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
CookieDomain
to.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
OlderCookieDomain
is not set, the refresh fails with a 500 error.- The user remains stuck until they clear cookies manually or
OlderCookieDomain
is set.
- The user remains stuck until they clear cookies manually or
- If
OlderCookieDomain
is 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.CreateNewSession
now defaults to the value of thest-auth-mode
header (if available) if the configuredconfig.GetTokenTransferMethod
returnsany
.- Enable smooth switching between
useDynamicAccessTokenSigningKey
settings by allowing refresh calls to change the signing key type of a session.
- Make session required during signout.
- Adds a type uint64 to the
accessTokenCookiesExpiryDurationMillis
local variable inrecipe/session/utils.go
. It also removes the redundantuint64
type forcing needed because of the untyped variable. - Fixes the passing of
tenantId
ingetAllSessionHandlesForUser
andrevokeAllSessionsForUser
based onfetchAcrossAllTenants
andrevokeAcrossAllTenants
inputs respectively. - Updated fake email generation
- Adds
TLSConfig
to SMTP settings. TLSConfig
is always passed to gomail so that it can be used when gomail usesSTARTTLS
to upgrade the connection to TLS. - #392- Not setting
InsecureSkipVerify
totrue
in 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
Origin
andGetOrigin
properties toAppInfo
, this can be configured to allow you to conditionally return the value of the frontend domain. This property will replaceWebsiteDomain
in a future release ofsupertokens-golang
WebsiteDomain
insideAppInfo
is now optional. UsingOrigin
orGetOrigin
is recommended over usingWebsiteDomain
. This is not a breaking change and usingWebsiteDomain
will 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
NetworkInterceptor
to theConnectionInfo
config. - This can be used to capture/modify all the HTTP requests sent to the core.
- Solves the issue - supertokens/supertokens-core#865
- Adds
debug
flag to theTypeInput
. If set totrue
, debug logs will be printed.
- Fixes an issue where sometimes the
Access-Control-Expose-Headers
header 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
ValidateAccessToken
to 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
timeJoined
casing in emailpassword and passwordless user objects.
- Solves issue with clock skew during third party sign-in/up - #362
- Bumped
github.com/golang-jtw/jwt
version from v4 to v5. - Bumped
github.com/MicahParks/keyfunc
version 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-Control
header to/jwt/jwks.json
(GetJWKSGET
) - Added
ValidityInSeconds
to the return value of the overrideableGetJWKS
function.- This can be used to control the
Cache-Control
header mentioned above. - It defaults to
60
or 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
500
status instead of panic whensupertokens.Middleware
is used without initializing the SDK. - Updates fiber adaptor package in the fiber example.
- The Dashboard recipe now accepts a new
Admins
property which can be used to give Dashboard Users write privileges for the user dashboard.
- Dashboard APIs now return a status code
403
for all non-GET requests if the currently logged in Dashboard User is not listed in theadmins
array - Now ignoring protected props in the payload in
CreateNewSession
andCreateNewSessionWithoutRequestResponse
- 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
tenantId
prefixed in the path. e.g. //signinup - Following recipe functions have been added:
emailpassword.CreateResetPasswordLink
emailpassword.SendResetPasswordEmail
emailverification.CreateEmailVerificationLink
emailverification.SendEmailVerificationEmail
thirdparty.GetProvider
thirdpartyemailpassword.ThirdPartyGetProvider
thirdpartyemailpassword.CreateResetPasswordLink
thirdpartyemailpassword.SendResetPasswordEmail
thirdpartypasswordless.ThirdPartyGetProvider
- Only supporting FDI 1.17
- Core must be upgraded to 6.0
- For consistency, all
UnknownUserIDError
have been renamed toUnknownUserIdError
getUsersOldestFirst
&getUsersNewestFirst
has mandatory parametertenantId
. Pass'public'
if not using multitenancy.- Added mandatory field
tenantId
toEmailDeliveryInterface
andSmsDeliveryInterface
. Pass'public'
if not using multitenancy. - Removed deprecated config
createAndSendCustomEmail
andcreateAndSendCustomTextMessage
. - EmailPassword recipe changes:
- Added mandatory
tenantId
field toTypeEmailPasswordPasswordResetEmailDeliveryInput
- Removed
resetPasswordUsingTokenFeature
fromTypeInput
- Added
tenantId
param tovalidate
function inTypeInputFormField
- Added mandatory
tenantId
as first parameter to the following recipe index functions:SignUp
SignIn
GetUserByEmail
CreateResetPasswordToken
ResetPasswordUsingToken
- Added mandatory
tenantId
in the input for the following recipe interface functions. If any of these functions are overridden, they need to be updated accordingly:SignUp
SignIn
GetUserByEmail
CreateResetPasswordToken
ResetPasswordUsingToken
UpdateEmailOrPassword
- Added mandatory
tenantId
in the input for the following API interface functions. If any of these functions are overridden, they need to be updated accordingly:EmailExistsGET
GeneratePasswordResetTokenPOST
PasswordResetPOST
SignInPOST
SignUpPOST
- Added mandatory
- EmailVerification recipe changes:
- Added mandatory
TenantId
field toEmailVerificationType
- Added mandatory
tenantId
as first parameter to the following recipe index functions:CreateEmailVerificationToken
VerifyEmailUsingToken
RevokeEmailVerificationTokens
- Added mandatory
tenantId
in the input for the following recipe interface functions. If any of these functions are overridden, they need to be updated accordingly:CreateEmailVerificationToken
VerifyEmailUsingToken
RevokeEmailVerificationTokens
- Added mandatory
tenantId
in 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
tenantId
param toValidateEmailAddress
,ValidatePhoneNumber
andGetCustomUserInputCode
functions inTypeInput
- Added mandatory
TenantId
field toemaildelivery.PasswordlessLoginType
andsmsdelivery.PasswordlessLoginType
- Added mandatory
tenantId
in the input to the following recipe index functions:CreateCodeWithEmail
CreateCodeWithPhoneNumber
CreateNewCodeForDevice
ConsumeCodeWithUserInputCode
ConsumeCodeWithLinkCode
GetUserByEmail
GetUserByPhoneNumber
RevokeAllCodesByEmail
RevokeAllCodesByPhoneNumber
RevokeCode
ListCodesByEmail
ListCodesByPhoneNumber
ListCodesByDeviceID
ListCodesByPreAuthSessionID
CreateMagicLinkByEmail
CreateMagicLinkByPhoneNumber
SignInUpByEmail
SignInUpByPhoneNumber
- Added mandatory
tenantId
in the input for the following recipe interface functions. If any of these functions are overridden, they need to be updated accordingly:CreateCode
CreateNewCodeForDevice
ConsumeCode
GetUserByEmail
GetUserByPhoneNumber
RevokeAllCodes
RevokeCode
ListCodesByEmail
ListCodesByPhoneNumber
ListCodesByDeviceID
ListCodesByPreAuthSessionID
- Added mandatory
tenantId
in the input for the following API interface functions. If any of these functions are overridden, they need to be updated accordingly:CreateCodePOST
ResendCodePOST
ConsumeCodePOST
EmailExistsGET
PhoneNumberExistsGET
- Added
- ThirdParty recipe changes
- The providers array in
SignInUpFeature
accepts[]ProviderInput
instead of[]TypeProvider
. TypeProvider interface is re-written. Refer migration section for more info. - Removed
SignInUp
and addedManuallyCreateOrUpdateUser
instead in the recipe index functions. - Added
ManuallyCreateOrUpdateUser
to recipe interface which is being called by the function mentioned above.ManuallyCreateOrUpdateUser
recipe interface function should not be overridden as it is not going to be called by the SDK in the sign in/up flow.SignInUp
recipe interface functions is not removed and is being used by the sign in/up flow.
- Added mandatory
tenantId
as first parameter to the following recipe index functions:GetUsersByEmail
GetUserByThirdPartyInfo
- Added mandatory
tenantId
in the input for the following recipe interface functions. If any of these functions are overridden, they need to be updated accordingly:GetUsersByEmail
GetUserByThirdPartyInfo
SignInUp
- Added mandatory
tenantId
in the input for the following API interface functions. If any of these functions are overridden, they need to be updated accordingly:AuthorisationUrlGET
SignInUpPOST
- Updated
SignInUp
recipe 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
AuthorisationUrlGET
API- Changed: Doesn't accept
clientId
anymore and acceptsclientType
instead to determine the matching config - Added: optional
pkceCodeVerifier
in the response, to support PKCE
- Changed: Doesn't accept
- Updated
SignInUpPOST
API- Removed:
clientId
,redirectURI
,authCodeResponse
andcode
from the input - Instead,
- accepts
clientType
to 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
state
parameter instead of using the websiteDomain config. - to use HTTP 303 instead of javascript based redirection.
- The providers array in
- Session recipe changes
- Added mandatory
tenantId
parameter to the following recipe index functions:CreateNewSession
CreateNewSessionWithoutRequestResponse
ValidateClaimsInJWTPayload
- Added mandatory
tenantId
in the input for the following recipe interface functions. If any of these functions are overridden, they need to be updated accordingly:CreateNewSession
GetGlobalClaimValidators
- Added
tenantId
andrevokeAcrossAllTenants
params toRevokeAllSessionsForUser
in the recipe interface. - Added
tenantId
andfetchAcrossAllTenants
params toGetAllSessionHandlesForUser
in the recipe interface. - Added
GetTenantId
function toTypeSessionContainer
- Added
tenantId
toFetchValue
function inPrimitiveClaim
,PrimitiveArrayClaim
.
- Added mandatory
- UserRoles recipe changes
- Added mandatory
tenantId
as first parameter to the following recipe index functions:AddRoleToUser
RemoveUserRole
GetRolesForUser
GetUsersThatHaveRole
- Added mandatory
tenantId
in the input for the following recipe interface functions. If any of these functions are overridden, they need to be updated accordingly:AddRoleToUser
RemoveUserRole
GetRolesForUser
GetUsersThatHaveRole
- 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
tenantIdForPasswordPolicy
param toemailpassword.UpdateEmailOrPassword
,thirdpartyemailpassword.UpdateEmailOrPassword
- Added optional param
tenantId
tosession.RevokeAllSessionsForUser
. If tenantId is nil, sessions are revoked across all tenants - Added optional param
tenantId
tosession.getAllSessionHandlesForUser
. If tenantId is nil, sessions handles across all tenants are returned
- Added optional
- Adds optional param
tenantId
toGetUserCount
which returns total count across all tenants if not passed. - Adds protected prop
tId
to the accessToken payload - Adds
IncludesAny
claim validator toPrimitiveArrayClaim
-
To call any recipe function that has
tenantId
added to it, pass'public
'Before:
emailpassword.SignUp("[email protected]", "password");
After:
emailpassword.SignUp("public", "[email protected]", "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
clientType
to differentiate them.clientType
passed from the frontend will be used to determine the right config.IsDefault
option has been removed andclientType
is expected to be passed when there are more than one client. If there is only one client,clientType
is 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
,UserInfoEndpointQueryParams
andUserInfoMap
to 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-configuration
to theoidcDiscoveryEndpoint
config. For eg. ifoidcDiscoveryEndpoint
is 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:
- 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
GetRequestFromUserContext
function 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
TryRefreshTokenError
orUnauthorizedError
to make debugging easier
- Adds additional tests for the session recipe
- Fixes #284
- Made the access token string optional in the overrideable
GetSession
function - Moved checking if the access token is nil into the overrideable
GetSession
function
- 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
GetSessionData
toGetSessionDataInDatabase
to clarify that it always hits the DB - Renamed
GetSessionDataWithContext
toGetSessionDataInDatabaseWithContext
to clarify that it always hits the DB - Renamed
UpdateSessionData
toUpdateSessionDataInDatabase
- Renamed
UpdateSessionDataWithContext
toUpdateSessionDataInDatabaseWithContext
to clarify that it always hits the DB - Renamed
SessionData
toSessionDataInDatabase
inSessionInformation
- Renamed
sessionData
tosessionDataInDatabase
in the input toCreateNewSession
- Added
useStaticSigningKey
toCreateJWT
andCreateJWTWithContext
- Added support for CDI version
2.21
- Dropped support for CDI version
2.8
-2.20
GetAccessTokenPayload
will 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
GetAccessToken
on the session) if you need a JWT - The
jwt
prop 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
AccessTokenPayload
toCustomClaimsInAccessTokenPayload
inSessionInformation
(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
useStaticSigningKey
param 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
GetAccessTokenLifeTimeMS
andGetRefreshTokenLifeTimeMS
functions - Added
ExposeAccessTokenToFrontendInCookieBasedAuth
(defaults tofalse
) option to the Session recipe config - Added new
checkDatabase
param toVerifySession
andGetSession
- Removed deprecated
UpdateAccessTokenPayload
,UpdateAccessTokenPayloadWithContext
,RegenerateAccessToken
andRegenerateAccessTokenWithContext
from the Session recipe interface - Added
CreateNewSessionWithoutRequestResponse
,CreateNewSessionWithContextWithoutRequestResponse
,GetSessionWithoutRequestResponse
,GetSessionWithContextWithoutRequestResponse
,RefreshSession
,RefreshSessionWithContextWithoutRequestResponse
to the Session recipe. - Added
GetAllSessionTokensDangerously
to session objects (SessionContainer
) - Added
AttachToRequestResponse
to 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: true
to 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
sub
in the code below, but you can replace it with another from the list if you used it in a custom access token payload.sub
iat
exp
sessionHandle
- On the frontend where you accessed the JWT before by:
(await Session.getAccessTokenPayloadSecurely()).jwt
update 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
CreateNewSession
function 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
iss
claim 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
GetSessionData
toGetSessionDataFromDatabase
to clarify that it always hits the DB - Renamed
UpdateSessionData
toUpdateSessionDataInDatabase
- Renamed
sessionData
tosessionDataInDatabase
inSessionInformation
and the input toCreateNewSession
- You should now set
CheckDatabase
to true in the verifySession params.
- You should now set
useDynamicAccessTokenSigningKey
in 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
sub
is the protected property we are updating by adding theapp
prefix):
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
GetUsersNewestFirst
andGetUsersOldestFirst
- 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
Authorization
header instead of cookies - Optional
GetTokenTransferMethod
config 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
CreateNewSession
now 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
CreateNewSessionWithContext
andCreateNewSession
in the session recipe accepts new- Only supporting FDI 1.16
parameter
req
of 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
Username
toSMTPSettings
, which can be used for SMTP login if username is different fromFrom.Email
.
- Fixes
newPassword
validation 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
. Mode
config is added toevmodels.TypeInput
GetEmailForUserID
config is added toevmodels.TypeInput
- Added
OnInvalidClaim
optional error handler to send InvalidClaim error responses. - Added
INVALID_CLAIMS
toSessionErrors
. - Added
InvalidClaimStatusCode
optional config to set the status code of InvalidClaim errors. - Added
OverrideGlobalClaimValidators
to options ofgetSession
andverifySession
. - Added
MergeIntoAccessTokenPayload
to the Session recipe and session objects which should be preferred to the now deprecatedUpdateAccessTokenPayload
. - Added
AssertClaims
,ValidateClaimsForSessionHandle
,ValidateClaimsInJWTPayload
to the Session recipe to support validation of the newly addedEmailVerificationClaim
. - Added
FetchAndSetClaim
,GetClaimValue
,SetClaimValue
andRemoveClaim
to the Session recipe to manage claims. - Added
AssertClaims
,FetchAndSetClaim
,GetClaimValue
,SetClaimValue
andRemoveClaim
to 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
UserRoleClaim
andPermissionClaim
to user roles recipe.
- Removes support for FDI < 1.15
- Changed
SignInUp
third 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
EmailVerification
recipe is now not initialized as part of auth recipes, it should be added to therecipeList
directly instead.- Email verification related overrides (
EmailVerificationFeature
prop ofOverride
) moved from auth recipes into theEmailVerification
recipe config. - ThirdParty recipe no longer takes EmailDelivery config -> use Emailverification recipe's EmailDelivery instead.
- Moved email verification related configs from the
EmailDelivery
config of auth recipes into a separateEmailVerification
email delivery config. - Updated return type of
GetEmailForUserId
in theEmailVerification
recipe config. It should now returnOK
,EmailDoesNotExistError
orUnknownUserIDError
as response. - Removed
GetResetPasswordURL
,GetEmailVerificationURL
,GetLinkDomainAndPath
. Changing these urls can be done in the email delivery configs instead. - Removed
UnverifyEmail
,RevokeEmailVerificationTokens
,IsEmailVerified
,VerifyEmailUsingToken
andCreateEmailVerificationToken
from auth recipes. These should be called on theEmailVerification
recipe instead. - Changed function signature for email verification APIs to accept a sessionContainer as an input.
- Changed Session API interface functions:
RefreshPOST
now returns a Session container object.SignOutPOST
now takes in an optional session object as a parameter.
SessionContainer
is renamed toTypeSessionContainer
andSessionContainer
is now an alias for*TypeSessionContainer
. All*SessionContainer
is now replaced withSessionContainer
.- Removed unused parameter
email
fromthirdpartyemailpassword.GetUserByThirdPartyInfoWithContext
function.
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
SkipAddingRolesToAccessToken
andSkipAddingPermissionsToAccessToken
to 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
,UpdateOrDeleteUserIdMappingInfo
functions 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
UNAUTHORISED
error when the input is a sessionHandle: https://github.com/supertokens/backend/issues/83 GetSessionInformation
now returnsnil
is the session does not existUpdateSessionData
now returnsnil
if the inputsessionHandle
does not exist.UpdateAccessTokenPayload
now returnsfalse
if the inputsessionHandle
does not exist.RegenerateAccessToken
now returnsnil
if the input access token'ssessionHandle
does not exist.- The session container functions have not changed in behaviour and return errors if
sessionHandle
does 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
SMTPServiceConfig
toSMTPSettings
- Changed type of
Secure
inSMTPSettings
from*bool
tobool
- Renamed
SMTPServiceFromConfig
toSMTPFrom
- Renamed
SMTPGetContentResult
toEmailContent
- Renamed
SMTPTypeInput
toSMTPServiceConfig
- Renamed field
SMTPSettings
toSettings
inSMTPServiceConfig
- Renamed
SMTPServiceInterface
toSMTPInterface
- Renamed all instances of
MakeSmtpService
toMakeSMTPService
- All instances of
MakeSMTPService
returns*EmailDeliveryInterface
instead ofEmailDeliveryInterface
- Renamed
TwilioServiceConfig
toTwilioSettings
- Renamed
TwilioGetContentResult
toSMSContent
- Renamed
TwilioTypeInput
toTwilioServiceConfig
- Renamed field
TwilioSettings
toSettings
inTwilioServiceConfig
- Changed types of fields
From
andMessagingServiceSid
inTwilioSettings
from*string
tostring
- Renamed
MakeSupertokensService
toMakeSupertokensSMSService
- All instances of
MakeSupertokensSMSService
andMakeTwilioService
returns*SmsDeliveryInterface
instead ofSmsDeliveryInterface
- Removed
SupertokensServiceConfig
andMakeSupertokensSMSService
acceptsapiKey
directly instead ofSupertokensServiceConfig
- Renamed
TwilioServiceInterface
toTwilioInterface
- Removes support for FDIs that are < 1.14
- Exposed
MakeSMTPService
from emailverification, emailpassword, passwordless, thirdparty, thirdpartyemailpassword and thirdpartypasswordless recipes - Exposed
MakeSupertokensSMSService
andMakeTwilioService
from passwordless and thirdpartypasswordless recipes
- Fixes Cookie SameSite config validation.
- Changes
getEmailForUserIdForEmailVerification
function 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.
EmailDelivery
user config for Emailpassword, Thirdparty, ThirdpartyEmailpassword, Passwordless and ThirdpartyPasswordless recipes.SmsDelivery
user config for Passwordless and ThirdpartyPasswordless recipes.Twilio
service integration for SmsDelivery ingredient.SMTP
service integration for EmailDelivery ingredient.Supertokens
service integration for SmsDelivery ingredient.
- For Emailpassword recipe input config,
ResetPasswordUsingTokenFeature.CreateAndSendCustomEmail
andEmailVerificationFeature.CreateAndSendCustomEmail
have been deprecated. - For Thirdparty recipe input config,
EmailVerificationFeature.CreateAndSendCustomEmail
has been deprecated. - For ThirdpartyEmailpassword recipe input config,
ResetPasswordUsingTokenFeature.CreateAndSendCustomEmail
andEmailVerificationFeature.CreateAndSendCustomEmail
have been deprecated. - For Passwordless recipe input config,
CreateAndSendCustomEmail
andCreateAndSendCustomTextMessage
have been deprecated. - For ThirdpartyPasswordless recipe input config,
CreateAndSendCustomEmail
,CreateAndSendCustomTextMessage
andEmailVerificationFeature.CreateAndSendCustomEmail
have 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_ERROR
output status in third party recipe API to beGENERAL_ERROR
. - Replaced
FIELD_ERROR
status type in third party signinup API withGENERAL_ERROR
. - Removed
FIELD_ERROR
status type from third party signinup recipe function.
- Adds
- Changes output of
VerifyEmailPOST
toVerifyEmailPOSTResponse
- Changes output of
PasswordResetPOST
toResetPasswordPOSTResponse
SignInUp
recipe function doesn't returnFIELD_ERROR
anymore in thirdparty, thirdpartypasswordless and thirdpartyemailpassword recipe.SignInUpPOST
api function returnsGENERAL_ERROR
instead ofFIELD_ERROR
in 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
GetJWKSGET
in JWT recipe to returnGetJWKSAPIResponse
(that also contains a General Error response) - Changes
GetOpenIdDiscoveryConfigurationGET
in Open ID recipe to returnGetOpenIdDiscoveryConfigurationAPIResponse
(that also contains a General Error response) - Renames
OnGeneralError
callback (that's in user input) toOnSuperTokensAPIError
- If there is an error in the
errorHandler
, we no longer callOnSuperTokensAPIError
in 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
GetUserByThirdPartyInfo
function in thethirdpartypasswordless
recipe.
- Fixes issue in writing custom response in API Override
- #102. Sending
preAuthSessionID
instead ofpreAuthSessionId
to the core. - Fixes the error message in AuthorizationUrlAPI function in the
api
module 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
->ThirdPartySignInUp
SignUp
->EmailPasswordSignUp
SignIn
->EmailPasswordSignIn
- API Interface:
EmailExistsGET
->EmailPasswordEmailExistsGET
- User exposed functions (in
recipe/thirdpartyemailpassword/main.go
)SignInUp
->ThirdPartySignInUp
SignUp
->EmailPasswordSignUp
SignIn
->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
regenerateAccessToken
as 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:
RevokeSession
andRevokeSessionWithContext
)
- 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
SendNon200Response
function: #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
keyfunc
dependency to stable version.
- Removes use of apiGatewayPath from apple's redirect URI since that is already there in the apiBasePath
- Changes
FIELD_ERROR
type in sign in up response fromError
toErrorMsg
- Sign in with google workspaces and discord
- If getting profile info from third party provider throws an error, that is propagated a
FIELD_ERROR
to the client.
- Does not send a response if the user has already sent the response: supertokens/supertokens-node#197
- When routing, ignores
rid
value"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 POST
API to takeauthCodeResponse
XORcode
so 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
getRedirectURI
function added to social providers in case we set theredirect_uri
on the backend. - Adds optional
IsDefault
param to auth providers so that they can be reused with different credentials. - Adds sign in with apple support: #20
- Removes
SignInUpPost
from thirdpartyemailpassword API interface and replaces it with three APIs:EmailPasswordSignInPOST
,EmailPasswordSignUpPOST
andThirdPartySignInUpPOST
: 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