Skip to content

Commit

Permalink
feat(jwt): add email claim to session JWT (#1404)
Browse files Browse the repository at this point in the history
* add email claim which contains email address, is_verified and is_primary values
* cleanup some unused stuff

Closes: #1388

Co-authored-by: Stefan Jacobi <[email protected]>
  • Loading branch information
shentschel and Stefan Jacobi authored Mar 25, 2024
1 parent b6a580b commit 8ab246d
Show file tree
Hide file tree
Showing 14 changed files with 196 additions and 124 deletions.
16 changes: 15 additions & 1 deletion backend/cmd/jwt/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"github.com/spf13/cobra"
"github.com/teamhanko/hanko/backend/config"
"github.com/teamhanko/hanko/backend/crypto/jwk"
"github.com/teamhanko/hanko/backend/dto"
"github.com/teamhanko/hanko/backend/persistence"
"github.com/teamhanko/hanko/backend/session"
"log"
Expand Down Expand Up @@ -52,7 +53,20 @@ func NewCreateCommand() *cobra.Command {
return
}

token, err := sessionManager.GenerateJWT(uuid.FromStringOrNil(args[0]))
userId := uuid.FromStringOrNil(args[0])

emails, err := persister.GetEmailPersister().FindByUserId(userId)
if err != nil {
fmt.Printf("failed to get emails from db: %s", err)
return
}

var emailJwt *dto.EmailJwt
if e := emails.GetPrimary(); e != nil {
emailJwt = dto.JwtFromEmailModel(e)
}

token, err := sessionManager.GenerateJWT(userId, emailJwt)
if err != nil {
fmt.Printf("failed to generate token: %s", err)
return
Expand Down
14 changes: 14 additions & 0 deletions backend/dto/email.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,17 @@ func FromEmailModel(email *models.Email) *EmailResponse {

return emailResponse
}

type EmailJwt struct {
Address string `json:"address"`
IsPrimary bool `json:"is_primary"`
IsVerified bool `json:"is_verified"`
}

func JwtFromEmailModel(email *models.Email) *EmailJwt {
return &EmailJwt{
Address: email.Address,
IsPrimary: email.IsPrimary(),
IsVerified: email.Verified,
}
}
8 changes: 4 additions & 4 deletions backend/handler/email_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ func (s *emailSuite) TestEmailHandler_List() {

for _, currentTest := range tests {
s.Run(currentTest.name, func() {
token, err := sessionManager.GenerateJWT(currentTest.userId)
token, err := sessionManager.GenerateJWT(currentTest.userId, nil)
s.Require().NoError(err)
cookie, err := sessionManager.GenerateCookie(token)
s.Require().NoError(err)
Expand Down Expand Up @@ -177,7 +177,7 @@ func (s *emailSuite) TestEmailHandler_Create() {
sessionManager, err := session.NewManager(jwkManager, cfg)
s.Require().NoError(err)

token, err := sessionManager.GenerateJWT(currentTest.userId)
token, err := sessionManager.GenerateJWT(currentTest.userId, nil)
s.Require().NoError(err)
cookie, err := sessionManager.GenerateCookie(token)
s.Require().NoError(err)
Expand Down Expand Up @@ -244,7 +244,7 @@ func (s *emailSuite) TestEmailHandler_SetPrimaryEmail() {
newPrimaryEmailId := uuid.FromStringOrNil("8bb4c8a7-a3e6-48bb-b54f-20e3b485ab33")
userId := uuid.FromStringOrNil("b5dd5267-b462-48be-b70d-bcd6f1bbe7a5")

token, err := sessionManager.GenerateJWT(userId)
token, err := sessionManager.GenerateJWT(userId, nil)
s.NoError(err)
cookie, err := sessionManager.GenerateCookie(token)
s.NoError(err)
Expand Down Expand Up @@ -285,7 +285,7 @@ func (s *emailSuite) TestEmailHandler_Delete() {
sessionManager, err := session.NewManager(jwkManager, test.DefaultConfig)
s.Require().NoError(err)

token, err := sessionManager.GenerateJWT(userId)
token, err := sessionManager.GenerateJWT(userId, nil)
s.NoError(err)
cookie, err := sessionManager.GenerateCookie(token)
s.NoError(err)
Expand Down
7 changes: 6 additions & 1 deletion backend/handler/passcode.go
Original file line number Diff line number Diff line change
Expand Up @@ -361,7 +361,12 @@ func (h *PasscodeHandler) Finish(c echo.Context) error {
}
}

token, err := h.sessionManager.GenerateJWT(passcode.UserId)
var emailJwt *dto.EmailJwt
if e := user.Emails.GetPrimary(); e != nil {
emailJwt = dto.JwtFromEmailModel(e)
}

token, err := h.sessionManager.GenerateJWT(passcode.UserId, emailJwt)
if err != nil {
return fmt.Errorf("failed to generate jwt: %w", err)
}
Expand Down
4 changes: 2 additions & 2 deletions backend/handler/passcode_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -298,13 +298,13 @@ func (s *passcodeSuite) TestPasscodeHandler_Finish() {
req := httptest.NewRequest(http.MethodPost, "/passcode/login/finalize", bytes.NewReader(bodyJson))
req.Header.Set("Content-Type", "application/json")
if currentTest.sendSessionTokenInAuthHeader {
sessionToken, err := sessionManager.GenerateJWT(uuid.FromStringOrNil(currentTest.userId))
sessionToken, err := sessionManager.GenerateJWT(uuid.FromStringOrNil(currentTest.userId), nil)
s.Require().NoError(err)
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", sessionToken))
}

if currentTest.sendSessionTokenInCookie {
sessionToken, err := sessionManager.GenerateJWT(uuid.FromStringOrNil(currentTest.userId))
sessionToken, err := sessionManager.GenerateJWT(uuid.FromStringOrNil(currentTest.userId), nil)
s.Require().NoError(err)

sessionCookie, err := sessionManager.GenerateCookie(sessionToken)
Expand Down
7 changes: 6 additions & 1 deletion backend/handler/password.go
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,12 @@ func (h *PasswordHandler) Login(c echo.Context) error {
return echo.NewHTTPError(http.StatusUnauthorized).SetInternal(err)
}

token, err := h.sessionManager.GenerateJWT(pw.UserId)
var emailJwt *dto.EmailJwt
if e := user.Emails.GetPrimary(); e != nil {
emailJwt = dto.JwtFromEmailModel(e)
}

token, err := h.sessionManager.GenerateJWT(pw.UserId, emailJwt)
if err != nil {
return fmt.Errorf("failed to generate jwt: %w", err)
}
Expand Down
14 changes: 7 additions & 7 deletions backend/handler/password_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ func (s *passwordSuite) TestPasswordHandler_Set_Create() {
s.Require().NoError(err)

sessionManager := s.GetDefaultSessionManager()
token, err := sessionManager.GenerateJWT(currentTest.userId)
token, err := sessionManager.GenerateJWT(currentTest.userId, nil)
s.Require().NoError(err)
cookie, err := sessionManager.GenerateCookie(token)
s.Require().NoError(err)
Expand Down Expand Up @@ -227,17 +227,17 @@ func TestMaxPasswordLength(t *testing.T) {
},
}

for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
hash, err := bcrypt.GenerateFromPassword([]byte(test.creationPassword), 12)
if test.wantErr {
for _, passwordTest := range tests {
t.Run(passwordTest.name, func(t *testing.T) {
hash, err := bcrypt.GenerateFromPassword([]byte(passwordTest.creationPassword), 12)
if passwordTest.wantErr {
assert.Error(t, err)
} else {
assert.NoError(t, err)
}

err = bcrypt.CompareHashAndPassword(hash, []byte(test.loginPassword))
if test.wantErr {
err = bcrypt.CompareHashAndPassword(hash, []byte(passwordTest.loginPassword))
if passwordTest.wantErr {
assert.Error(t, err)
} else {
assert.NoError(t, err)
Expand Down
12 changes: 11 additions & 1 deletion backend/handler/token.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,17 @@ func (h TokenHandler) Validate(c echo.Context) error {
return fmt.Errorf("failed to delete token from db: %w", terr)
}

jwtToken, err := h.sessionManager.GenerateJWT(token.UserID)
emails, err := h.persister.GetEmailPersister().FindByUserId(token.UserID)
if err != nil {
return fmt.Errorf("failed to get emails from db: %w", err)
}

var emailJwt *dto.EmailJwt
if e := emails.GetPrimary(); e != nil {
emailJwt = dto.JwtFromEmailModel(e)
}

jwtToken, err := h.sessionManager.GenerateJWT(token.UserID, emailJwt)
if err != nil {
return fmt.Errorf("failed to generate jwt: %w", err)
}
Expand Down
8 changes: 7 additions & 1 deletion backend/handler/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,13 @@ func (h *UserHandler) Create(c echo.Context) error {
return fmt.Errorf("failed to store primary email: %w", err)
}

token, err := h.sessionManager.GenerateJWT(newUser.ID)
var emailJwt *dto.EmailJwt
if e := newUser.Emails.GetPrimary(); e != nil {
emailJwt = dto.JwtFromEmailModel(e)
}

token, err := h.sessionManager.GenerateJWT(newUser.ID, emailJwt)

if err != nil {
return fmt.Errorf("failed to generate jwt: %w", err)
}
Expand Down
12 changes: 6 additions & 6 deletions backend/handler/user_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,7 @@ func (s *userSuite) TestUserHandler_Get() {
if err != nil {
panic(fmt.Errorf("failed to create session generator: %w", err))
}
token, err := sessionManager.GenerateJWT(uuid.FromStringOrNil(userId))
token, err := sessionManager.GenerateJWT(uuid.FromStringOrNil(userId), nil)
s.Require().NoError(err)
cookie, err := sessionManager.GenerateCookie(token)
s.Require().NoError(err)
Expand Down Expand Up @@ -301,7 +301,7 @@ func (s *userSuite) TestUserHandler_GetUserWithWebAuthnCredential() {
if err != nil {
panic(fmt.Errorf("failed to create session generator: %w", err))
}
token, err := sessionManager.GenerateJWT(uuid.FromStringOrNil(userId))
token, err := sessionManager.GenerateJWT(uuid.FromStringOrNil(userId), nil)
s.Require().NoError(err)
cookie, err := sessionManager.GenerateCookie(token)
s.Require().NoError(err)
Expand Down Expand Up @@ -338,7 +338,7 @@ func (s *userSuite) TestUserHandler_Get_InvalidUserId() {
if err != nil {
panic(fmt.Errorf("failed to create session generator: %w", err))
}
token, err := sessionManager.GenerateJWT(uuid.FromStringOrNil(userId))
token, err := sessionManager.GenerateJWT(uuid.FromStringOrNil(userId), nil)
s.Require().NoError(err)
cookie, err := sessionManager.GenerateCookie(token)
s.Require().NoError(err)
Expand Down Expand Up @@ -475,7 +475,7 @@ func (s *userSuite) TestUserHandler_Me() {
if err != nil {
panic(fmt.Errorf("failed to create session generator: %w", err))
}
token, err := sessionManager.GenerateJWT(uuid.FromStringOrNil(userId))
token, err := sessionManager.GenerateJWT(uuid.FromStringOrNil(userId), nil)
s.Require().NoError(err)
cookie, err := sessionManager.GenerateCookie(token)
s.Require().NoError(err)
Expand Down Expand Up @@ -512,7 +512,7 @@ func (s *userSuite) TestUserHandler_Logout() {
if err != nil {
panic(fmt.Errorf("failed to create session generator: %w", err))
}
token, err := sessionManager.GenerateJWT(userId)
token, err := sessionManager.GenerateJWT(userId, nil)
s.Require().NoError(err)
cookie, err := sessionManager.GenerateCookie(token)
s.Require().NoError(err)
Expand Down Expand Up @@ -553,7 +553,7 @@ func (s *userSuite) TestUserHandler_Delete() {
if err != nil {
panic(fmt.Errorf("failed to create session generator: %w", err))
}
token, err := sessionManager.GenerateJWT(userId)
token, err := sessionManager.GenerateJWT(userId, nil)
s.Require().NoError(err)
cookie, err := sessionManager.GenerateCookie(token)
s.Require().NoError(err)
Expand Down
Loading

0 comments on commit 8ab246d

Please sign in to comment.