Skip to content

Commit e8d2ea7

Browse files
Merge pull request #163 from supertokens/session-claims
feat: session claims
2 parents b3b3525 + 4101c14 commit e8d2ea7

File tree

133 files changed

+6041
-2417
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

133 files changed

+6041
-2417
lines changed

CHANGELOG.md

Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,184 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [unreleased]
99

10+
### Added
11+
12+
- Added support for session claims with related interfaces and classes.
13+
- Added `EmailVerificationClaim`.
14+
- `Mode` config is added to `evmodels.TypeInput`
15+
- `GetEmailForUserID` config is added to `evmodels.TypeInput`
16+
- Added `OnInvalidClaim` optional error handler to send InvalidClaim error responses.
17+
- Added `INVALID_CLAIMS` to `SessionErrors`.
18+
- Added `InvalidClaimStatusCode` optional config to set the status code of InvalidClaim errors.
19+
- Added `OverrideGlobalClaimValidators` to options of `getSession` and `verifySession`.
20+
- Added `MergeIntoAccessTokenPayload` to the Session recipe and session objects which should be preferred to the now deprecated `UpdateAccessTokenPayload`.
21+
- Added `AssertClaims`, `ValidateClaimsForSessionHandle`, `ValidateClaimsInJWTPayload` to the Session recipe to support validation of the newly added `EmailVerificationClaim`.
22+
- Added `FetchAndSetClaim`, `GetClaimValue`, `SetClaimValue` and `RemoveClaim` to the Session recipe to manage claims.
23+
- Added `AssertClaims`, `FetchAndSetClaim`, `GetClaimValue`, `SetClaimValue` and `RemoveClaim` to session objects to manage claims.
24+
- Added sessionContainer to the input of `GenerateEmailVerifyTokenPOST`, `VerifyEmailPOST`, `IsEmailVerifiedGET`.
25+
- Adds default UserContext for verifySession calls that contains the request object.
26+
- Added `UserRoleClaim` and `PermissionClaim` to user roles recipe.
27+
28+
### Breaking changes
29+
- Changed `SignInUp` third party recipe function to accept an email string instead of an object that takes `{ID: string, IsVerified: boolean}`.
30+
- The frontend SDK should be updated to a version supporting session claims!
31+
- supertokens-auth-react: >= 0.25.0
32+
- supertokens-web-js: >= 0.2.0
33+
- `EmailVerification` recipe is now not initialized as part of auth recipes, it should be added to the `recipeList` directly instead.
34+
- Email verification related overrides (`EmailVerificationFeature` prop of `Override`) moved from auth recipes into the `EmailVerification` recipe config.
35+
- ThirdParty recipe no longer takes EmailDelivery config -> use Emailverification recipe's EmailDelivery instead.
36+
- Moved email verification related configs from the `EmailDelivery` config of auth recipes into a separate `EmailVerification` email delivery config.
37+
- Updated return type of `GetEmailForUserId` in the `EmailVerification` recipe config. It should now return `OK`, `EmailDoesNotExistError` or `UnknownUserIDError` as response.
38+
- Removed `GetResetPasswordURL`, `GetEmailVerificationURL`, `GetLinkDomainAndPath`. Changing these urls can be done in the email delivery configs instead.
39+
- Removed `UnverifyEmail`, `RevokeEmailVerificationTokens`, `IsEmailVerified`, `VerifyEmailUsingToken` and `CreateEmailVerificationToken` from auth recipes. These should be called on the `EmailVerification` recipe instead.
40+
- Changed function signature for email verification APIs to accept a sessionContainer as an input.
41+
- Changed Session API interface functions:
42+
- `RefreshPOST` now returns a Session container object.
43+
- `SignOutPOST` now takes in an optional session object as a parameter.
44+
- `SessionContainer` is renamed to `TypeSessionContainer` and `SessionContainer` is now an alias for `*TypeSessionContainer`. All `*SessionContainer` is now replaced with `SessionContainer`.
45+
- Removed unused parameter `email` from `thirdpartyemailpassword.GetUserByThirdPartyInfoWithContext` function.
46+
47+
### Migration
48+
49+
Before:
50+
51+
```go
52+
53+
supertokens.Init(supertokens.TypeInput{
54+
AppInfo: supertokens.AppInfo{
55+
AppName: "...",
56+
APIDomain: "...",
57+
WebsiteDomain: "...",
58+
},
59+
60+
RecipeList: []supertokens.Recipe{
61+
emailpassword.Init(&epmodels.TypeInput{
62+
EmailVerificationFeature: evmodels.TypeInput{
63+
// ...
64+
},
65+
Override: &epmodels.OverrideStruct{
66+
EmailVerificationFeature: &evmodels.OverrideStruct{
67+
// ...
68+
},
69+
},
70+
}),
71+
},
72+
})
73+
74+
```
75+
76+
After the update:
77+
78+
```go
79+
80+
supertokens.Init(supertokens.TypeInput{
81+
AppInfo: supertokens.AppInfo{
82+
AppName: "...",
83+
APIDomain: "...",
84+
WebsiteDomain: "...",
85+
},
86+
87+
RecipeList: []supertokens.Recipe{
88+
emailverification.Init(evmodels.TypeInput{
89+
Mode: evmodels.ModeRequired, // or evmodels.ModeOptional
90+
// all config should be moved here from the emailVerificationFeature prop of the EmailPassword recipe config
91+
Override: &evmodels.OverrideStruct{
92+
// move the overrides from the emailVerificationFeature prop of the override config in the EmailPassword init here
93+
},
94+
}),
95+
emailpassword.Init(nil),
96+
},
97+
})
98+
99+
```
100+
101+
### Passwordless users and email verification
102+
103+
If you turn on email verification your email-based passwordless users may be redirected to an email verification screen in their existing session.
104+
Logging out and logging in again will solve this problem or they could click the link in the email to verify themselves.
105+
106+
You can avoid this by running a script that will:
107+
108+
1. list all users of passwordless
109+
2. create an emailverification token for each of them if they have email addresses
110+
3. user the token to verify their address
111+
112+
Something similar to this script:
113+
114+
```go
115+
package main
116+
117+
import (
118+
"github.com/supertokens/supertokens-golang/recipe/emailverification"
119+
"github.com/supertokens/supertokens-golang/recipe/emailverification/evmodels"
120+
"github.com/supertokens/supertokens-golang/recipe/passwordless"
121+
"github.com/supertokens/supertokens-golang/recipe/passwordless/plessmodels"
122+
"github.com/supertokens/supertokens-golang/recipe/session"
123+
"github.com/supertokens/supertokens-golang/supertokens"
124+
)
125+
126+
func main() {
127+
supertokens.Init(supertokens.TypeInput{
128+
AppInfo: supertokens.AppInfo{
129+
AppName: "...",
130+
APIDomain: "...",
131+
WebsiteDomain: "...",
132+
},
133+
134+
RecipeList: []supertokens.Recipe{
135+
emailverification.Init(evmodels.TypeInput{
136+
Mode: evmodels.ModeRequired,
137+
}),
138+
passwordless.Init(plessmodels.TypeInput{
139+
FlowType: "USER_INPUT_CODE_AND_MAGIC_LINK",
140+
ContactMethodEmailOrPhone: plessmodels.ContactMethodEmailOrPhoneConfig{
141+
Enabled: true,
142+
},
143+
}),
144+
session.Init(nil),
145+
},
146+
})
147+
148+
var paginationToken *string
149+
recipeList := []string{"passwordless"}
150+
limit := 100
151+
done := false
152+
153+
for !done {
154+
userList, err := supertokens.GetUsersNewestFirst(paginationToken, &limit, &recipeList)
155+
if err != nil {
156+
panic(err)
157+
}
158+
159+
for _, user := range userList.Users {
160+
if user.RecipeId == "passwordless" && user.User["email"] != nil {
161+
token, err := emailverification.CreateEmailVerificationToken(user.User["id"].(string), nil)
162+
if err != nil {
163+
panic(err)
164+
}
165+
if token.OK != nil {
166+
_, err := emailverification.VerifyEmailUsingToken(token.OK.Token)
167+
if err != nil {
168+
panic(err)
169+
}
170+
}
171+
}
172+
173+
done = (userList.NextPaginationToken == nil)
174+
paginationToken = userList.NextPaginationToken
175+
}
176+
}
177+
}
178+
```
179+
180+
#### User roles
181+
182+
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.
183+
184+
- You can disable this behaviour by setting `SkipAddingRolesToAccessToken` and `SkipAddingPermissionsToAccessToken` to true in the recipe init.
185+
- Check how to use the new claims in the updated guide: https://supertokens.com/docs/userroles/protecting-routes
186+
187+
10188
## [0.8.3] - 2022-07-30
11189
### Added
12190
- Adds test to verify that session container uses overridden functions

examples/with-chi-oso/main.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import (
88
"github.com/go-chi/cors"
99
"github.com/supertokens/supertokens-golang/examples/with-chi-oso/database"
1010
"github.com/supertokens/supertokens-golang/examples/with-chi-oso/service"
11+
"github.com/supertokens/supertokens-golang/recipe/emailverification"
12+
"github.com/supertokens/supertokens-golang/recipe/emailverification/evmodels"
1113
"github.com/supertokens/supertokens-golang/recipe/session"
1214
"github.com/supertokens/supertokens-golang/recipe/thirdparty"
1315
"github.com/supertokens/supertokens-golang/recipe/thirdparty/tpmodels"
@@ -32,6 +34,9 @@ func main() {
3234
WebsiteDomain: "http://localhost:3000",
3335
},
3436
RecipeList: []supertokens.Recipe{
37+
emailverification.Init(evmodels.TypeInput{
38+
Mode: evmodels.ModeRequired,
39+
}),
3540
thirdpartyemailpassword.Init(&tpepmodels.TypeInput{
3641
Providers: []tpmodels.TypeProvider{
3742
// We have provided you with development keys which you can use for testsing.

examples/with-chi/main.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import (
66

77
"github.com/go-chi/chi/v5"
88
"github.com/go-chi/cors"
9+
"github.com/supertokens/supertokens-golang/recipe/emailverification"
10+
"github.com/supertokens/supertokens-golang/recipe/emailverification/evmodels"
911
"github.com/supertokens/supertokens-golang/recipe/session"
1012
"github.com/supertokens/supertokens-golang/recipe/thirdparty"
1113
"github.com/supertokens/supertokens-golang/recipe/thirdparty/tpmodels"
@@ -25,6 +27,9 @@ func main() {
2527
WebsiteDomain: "http://localhost:3000",
2628
},
2729
RecipeList: []supertokens.Recipe{
30+
emailverification.Init(evmodels.TypeInput{
31+
Mode: evmodels.ModeRequired,
32+
}),
2833
thirdpartyemailpassword.Init(&tpepmodels.TypeInput{
2934
/*
3035
We use different credentials for different platforms when required. For example the redirect URI for Github

examples/with-fiber/main.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import (
77
"github.com/gofiber/adaptor/v2"
88
"github.com/gofiber/fiber/v2"
99
"github.com/gofiber/fiber/v2/middleware/cors"
10+
"github.com/supertokens/supertokens-golang/recipe/emailverification"
11+
"github.com/supertokens/supertokens-golang/recipe/emailverification/evmodels"
1012
"github.com/supertokens/supertokens-golang/recipe/session"
1113
"github.com/supertokens/supertokens-golang/recipe/session/sessmodels"
1214
"github.com/supertokens/supertokens-golang/recipe/thirdparty"
@@ -28,6 +30,9 @@ func main() {
2830
WebsiteDomain: "http://localhost:3000",
2931
},
3032
RecipeList: []supertokens.Recipe{
33+
emailverification.Init(evmodels.TypeInput{
34+
Mode: evmodels.ModeRequired,
35+
}),
3136
thirdpartyemailpassword.Init(&tpepmodels.TypeInput{
3237
/*
3338
We use different credentials for different platforms when required. For example the redirect URI for Github
@@ -113,7 +118,7 @@ func main() {
113118
log.Fatal(app.Listen(":3001"))
114119
}
115120

116-
//wrapper of the original implementation of verify session to match the required function signature
121+
// wrapper of the original implementation of verify session to match the required function signature
117122
func verifySession(options *sessmodels.VerifySessionOptions) fiber.Handler {
118123
return func(c *fiber.Ctx) error {
119124
return adaptor.HTTPHandlerFunc(http.HandlerFunc(session.VerifySession(options, func(rw http.ResponseWriter, r *http.Request) {
@@ -156,7 +161,7 @@ func sessioninfo(c *fiber.Ctx) error {
156161
} else {
157162
counter = int(counter.(float64) + 1)
158163
}
159-
err = sessionContainer.UpdateAccessTokenPayload(map[string]interface{}{
164+
err = sessionContainer.MergeIntoAccessTokenPayload(map[string]interface{}{
160165
"counter": counter.(int),
161166
})
162167
if err != nil {
@@ -170,7 +175,7 @@ func sessioninfo(c *fiber.Ctx) error {
170175
})
171176
}
172177

173-
//utility funtion to help convert an array of string to convert to comma separeted string format
178+
// utility funtion to help convert an array of string to convert to comma separeted string format
174179
func stringArrayToStringConvertor(stringArray []string) string {
175180
var stringToBeReturned string
176181
for _, val := range stringArray {

examples/with-gin/config/config.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import (
44
"log"
55

66
"github.com/spf13/viper"
7+
"github.com/supertokens/supertokens-golang/recipe/emailverification"
8+
"github.com/supertokens/supertokens-golang/recipe/emailverification/evmodels"
79
"github.com/supertokens/supertokens-golang/recipe/session"
810
"github.com/supertokens/supertokens-golang/recipe/thirdparty"
911
"github.com/supertokens/supertokens-golang/recipe/thirdparty/tpmodels"
@@ -38,6 +40,9 @@ func Init() {
3840
WebsiteDomain: "http://localhost" + config.GetString("server.websitePort"),
3941
},
4042
RecipeList: []supertokens.Recipe{
43+
emailverification.Init(evmodels.TypeInput{
44+
Mode: evmodels.ModeRequired,
45+
}),
4146
thirdpartyemailpassword.Init(&tpepmodels.TypeInput{
4247
/*
4348
We use different credentials for different platforms when required. For example the redirect URI for Github

examples/with-go-zero/main.go

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import (
66
"net/http"
77
"strings"
88

9+
"github.com/supertokens/supertokens-golang/recipe/emailverification"
10+
"github.com/supertokens/supertokens-golang/recipe/emailverification/evmodels"
911
"github.com/supertokens/supertokens-golang/recipe/session"
1012
"github.com/supertokens/supertokens-golang/recipe/thirdparty"
1113
"github.com/supertokens/supertokens-golang/recipe/thirdparty/tpmodels"
@@ -26,6 +28,9 @@ func main() {
2628
WebsiteDomain: "http://localhost:3000",
2729
},
2830
RecipeList: []supertokens.Recipe{
31+
emailverification.Init(evmodels.TypeInput{
32+
Mode: evmodels.ModeRequired,
33+
}),
2934
thirdpartyemailpassword.Init(&tpepmodels.TypeInput{
3035
/*
3136
We use different credentials for different platforms when required. For example the redirect URI for Github
@@ -95,7 +100,7 @@ func main() {
95100
server := rest.MustNewServer(
96101
rest.RestConf{
97102
Host: "0.0.0.0",
98-
Port: 8000,
103+
Port: 3001,
99104
},
100105
)
101106
defer server.Stop()
@@ -105,11 +110,20 @@ func main() {
105110
addSupertokensRoutes("/auth", server) // go-zero doesn't execute middlewares if matching routes are not found
106111
server.AddRoute(rest.Route{
107112
Method: http.MethodGet,
108-
Path: "/sessionInfo",
113+
Path: "/sessioninfo",
109114
Handler: func(rw http.ResponseWriter, r *http.Request) {
110115
session.VerifySession(nil, sessioninfo).ServeHTTP(rw, r)
111116
},
112117
})
118+
119+
// go-zero does not honour middlewares for CORS requests unless we explicitly add a route for it.
120+
server.AddRoute(rest.Route{
121+
Method: http.MethodOptions,
122+
Path: "/sessioninfo",
123+
Handler: func(rw http.ResponseWriter, r *http.Request) {
124+
rw.WriteHeader(http.StatusOK)
125+
},
126+
})
113127
server.Start()
114128
}
115129

@@ -148,12 +162,11 @@ func sessioninfo(w http.ResponseWriter, r *http.Request) {
148162

149163
func corsMiddleware(next http.HandlerFunc) http.HandlerFunc {
150164
return func(response http.ResponseWriter, r *http.Request) {
151-
response.Header().Set("Access-Control-Allow-Origin", "localhost:8000")
165+
response.Header().Set("Access-Control-Allow-Origin", "http://localhost:3000")
152166
response.Header().Set("Access-Control-Allow-Credentials", "true")
153167
if r.Method == "OPTIONS" {
154168
response.Header().Set("Access-Control-Allow-Headers", strings.Join(append([]string{"Content-Type"}, supertokens.GetAllCORSHeaders()...), ","))
155-
response.Header().Set("Access-Control-Allow-Methods", "*")
156-
response.Write([]byte(""))
169+
response.Header().Set("Access-Control-Allow-Methods", "GET,POST,PUT,HEAD,OPTIONS")
157170
} else {
158171
next.ServeHTTP(response, r)
159172
}

examples/with-http/main.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import (
55
"net/http"
66
"strings"
77

8+
"github.com/supertokens/supertokens-golang/recipe/emailverification"
9+
"github.com/supertokens/supertokens-golang/recipe/emailverification/evmodels"
810
"github.com/supertokens/supertokens-golang/recipe/session"
911
"github.com/supertokens/supertokens-golang/recipe/thirdparty"
1012
"github.com/supertokens/supertokens-golang/recipe/thirdparty/tpmodels"
@@ -24,6 +26,9 @@ func main() {
2426
WebsiteDomain: "http://localhost:3000",
2527
},
2628
RecipeList: []supertokens.Recipe{
29+
emailverification.Init(evmodels.TypeInput{
30+
Mode: evmodels.ModeRequired,
31+
}),
2732
thirdpartyemailpassword.Init(&tpepmodels.TypeInput{
2833
/*
2934
We use different credentials for different platforms when required. For example the redirect URI for Github

examples/with-labstack-echo/main.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import (
77
"strings"
88

99
"github.com/labstack/echo/v4"
10+
"github.com/supertokens/supertokens-golang/recipe/emailverification"
11+
"github.com/supertokens/supertokens-golang/recipe/emailverification/evmodels"
1012
"github.com/supertokens/supertokens-golang/recipe/session"
1113
"github.com/supertokens/supertokens-golang/recipe/session/sessmodels"
1214
"github.com/supertokens/supertokens-golang/recipe/thirdparty"
@@ -27,6 +29,9 @@ func main() {
2729
WebsiteDomain: "http://localhost:3000",
2830
},
2931
RecipeList: []supertokens.Recipe{
32+
emailverification.Init(evmodels.TypeInput{
33+
Mode: evmodels.ModeRequired,
34+
}),
3035
thirdpartyemailpassword.Init(&tpepmodels.TypeInput{
3136
/*
3237
We use different credentials for different platforms when required. For example the redirect URI for Github
@@ -144,7 +149,7 @@ func verifySession(options *sessmodels.VerifySessionOptions) echo.MiddlewareFunc
144149
}
145150

146151
func sessioninfo(c echo.Context) error {
147-
sessionContainer := c.Get("session").(*sessmodels.SessionContainer)
152+
sessionContainer := c.Get("session").(sessmodels.SessionContainer)
148153

149154
if sessionContainer == nil {
150155
return errors.New("no session found")

0 commit comments

Comments
 (0)