diff --git a/README.md b/README.md index 127231ee6..16063e468 100644 --- a/README.md +++ b/README.md @@ -152,6 +152,10 @@ How long tokens are valid for, in seconds. Defaults to 3600 (1 hour). The default JWT audience. Use audiences to group users. +`JWT_ISS` - `string` + +The "iss" (issuer) claim identifies the principal that issued the JWT + `JWT_ADMIN_GROUP_NAME` - `string` The name of the admin group (if enabled). Defaults to `admin`. diff --git a/api/admin_test.go b/api/admin_test.go index 684d40989..a24eecc92 100644 --- a/api/admin_test.go +++ b/api/admin_test.go @@ -55,7 +55,7 @@ func (ts *AdminTestSuite) makeSuperAdmin(email string) string { u.IsSuperAdmin = true require.NoError(ts.T(), ts.API.db.Create(u), "Error creating user") - token, err := generateAccessToken(u, time.Second*time.Duration(ts.Config.JWT.Exp), ts.Config.JWT.Secret) + token, err := generateAccessToken(u, time.Second*time.Duration(ts.Config.JWT.Exp), ts.Config.JWT.Secret, ts.Config.JWT.Iss) require.NoError(ts.T(), err, "Error generating access token") p := jwt.Parser{ValidMethods: []string{jwt.SigningMethodHS256.Name}} @@ -70,7 +70,7 @@ func (ts *AdminTestSuite) makeSuperAdmin(email string) string { func (ts *AdminTestSuite) makeSystemUser() string { u := models.NewSystemUser(uuid.Nil, ts.Config.JWT.Aud) - token, err := generateAccessToken(u, time.Second*time.Duration(ts.Config.JWT.Exp), ts.Config.JWT.Secret) + token, err := generateAccessToken(u, time.Second*time.Duration(ts.Config.JWT.Exp), ts.Config.JWT.Secret, ts.Config.JWT.Iss) require.NoError(ts.T(), err, "Error generating access token") p := jwt.Parser{ValidMethods: []string{jwt.SigningMethodHS256.Name}} diff --git a/api/audit_test.go b/api/audit_test.go index 8ddc1f8f0..b4c66f8d3 100644 --- a/api/audit_test.go +++ b/api/audit_test.go @@ -52,7 +52,7 @@ func (ts *AuditTestSuite) makeSuperAdmin(email string) string { u.IsSuperAdmin = true require.NoError(ts.T(), ts.API.db.Create(u), "Error creating user") - token, err := generateAccessToken(u, time.Second*time.Duration(ts.Config.JWT.Exp), ts.Config.JWT.Secret) + token, err := generateAccessToken(u, time.Second*time.Duration(ts.Config.JWT.Exp), ts.Config.JWT.Secret, ts.Config.JWT.Iss) require.NoError(ts.T(), err, "Error generating access token") p := jwt.Parser{ValidMethods: []string{jwt.SigningMethodHS256.Name}} diff --git a/api/invite_test.go b/api/invite_test.go index 1e7648812..cd4650f54 100644 --- a/api/invite_test.go +++ b/api/invite_test.go @@ -61,7 +61,7 @@ func (ts *InviteTestSuite) makeSuperAdmin(email string) string { u.IsSuperAdmin = true require.NoError(ts.T(), ts.API.db.Create(u), "Error creating user") - token, err := generateAccessToken(u, time.Second*time.Duration(ts.Config.JWT.Exp), ts.Config.JWT.Secret) + token, err := generateAccessToken(u, time.Second*time.Duration(ts.Config.JWT.Exp), ts.Config.JWT.Secret, ts.Config.JWT.Iss) require.NoError(ts.T(), err, "Error generating access token") p := jwt.Parser{ValidMethods: []string{jwt.SigningMethodHS256.Name}} diff --git a/api/token.go b/api/token.go index c999377ea..ca0e5d2f4 100644 --- a/api/token.go +++ b/api/token.go @@ -139,7 +139,7 @@ func (a *API) RefreshTokenGrant(ctx context.Context, w http.ResponseWriter, r *h return internalServerError(terr.Error()) } - tokenString, terr = generateAccessToken(user, time.Second*time.Duration(config.JWT.Exp), config.JWT.Secret) + tokenString, terr = generateAccessToken(user, time.Second*time.Duration(config.JWT.Exp), config.JWT.Secret, config.JWT.Iss) if terr != nil { return internalServerError("error generating jwt token").WithInternalError(terr) } @@ -163,12 +163,13 @@ func (a *API) RefreshTokenGrant(ctx context.Context, w http.ResponseWriter, r *h }) } -func generateAccessToken(user *models.User, expiresIn time.Duration, secret string) (string, error) { +func generateAccessToken(user *models.User, expiresIn time.Duration, secret string, iss string) (string, error) { claims := &GoTrueClaims{ StandardClaims: jwt.StandardClaims{ Subject: user.ID.String(), Audience: user.Aud, ExpiresAt: time.Now().Add(expiresIn).Unix(), + Issuer: Iss, }, Email: user.Email, AppMetaData: user.AppMetaData, @@ -195,7 +196,7 @@ func (a *API) issueRefreshToken(ctx context.Context, conn *storage.Connection, u return internalServerError("Database error granting user").WithInternalError(terr) } - tokenString, terr = generateAccessToken(user, time.Second*time.Duration(config.JWT.Exp), config.JWT.Secret) + tokenString, terr = generateAccessToken(user, time.Second*time.Duration(config.JWT.Exp), config.JWT.Secret, config.JWT.Iss) if terr != nil { return internalServerError("error generating jwt token").WithInternalError(terr) } diff --git a/api/user_test.go b/api/user_test.go index 85d6ed54a..96abb2833 100644 --- a/api/user_test.go +++ b/api/user_test.go @@ -62,7 +62,7 @@ func (ts *UserTestSuite) TestUser_UpdatePassword() { req := httptest.NewRequest(http.MethodPut, "http://localhost/user", &buffer) req.Header.Set("Content-Type", "application/json") - token, err := generateAccessToken(u, time.Second*time.Duration(ts.Config.JWT.Exp), ts.Config.JWT.Secret) + token, err := generateAccessToken(u, time.Second*time.Duration(ts.Config.JWT.Exp), ts.Config.JWT.Secret, ts.Config.JWT.Iss) require.NoError(ts.T(), err) req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token)) diff --git a/conf/configuration.go b/conf/configuration.go index ac0b5fb9d..d3f98a608 100644 --- a/conf/configuration.go +++ b/conf/configuration.go @@ -44,6 +44,7 @@ type DBConfiguration struct { // JWTConfiguration holds all the JWT related configuration. type JWTConfiguration struct { Secret string `json:"secret" required:"true"` + Iss string `json:"iss"` Exp int `json:"exp"` Aud string `json:"aud"` AdminGroupName string `json:"admin_group_name" split_words:"true"` diff --git a/example.env b/example.env index c5618218a..d4542392b 100644 --- a/example.env +++ b/example.env @@ -1,6 +1,7 @@ GOTRUE_JWT_SECRET="CHANGE-THIS! VERY IMPORTANT!" GOTRUE_JWT_EXP=3600 GOTRUE_JWT_AUD=api.netlify.com +GOTRUE_JWT_ISS=api.netlify.com GOTRUE_DB_DRIVER=mysql DATABASE_URL="root@tcp(127.0.0.1:3306)/gotrue_development?parseTime=true&multiStatements=true" GOTRUE_API_HOST=localhost diff --git a/hack/test.env b/hack/test.env index f6e5a45a1..4fb7ef185 100644 --- a/hack/test.env +++ b/hack/test.env @@ -1,6 +1,7 @@ GOTRUE_JWT_SECRET=testsecret GOTRUE_JWT_EXP=3600 GOTRUE_JWT_AUD=api.netlify.com +GOTRUE_JWT_ISS=api.netlify.com GOTRUE_DB_DRIVER=mysql GOTRUE_DB_AUTOMIGRATE=true GOTRUE_DB_NAMESPACE=test