Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(webauthn): change user_id from uuid.UUID to string #9

Merged
merged 5 commits into from
Nov 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion server/api/dto/intern/webauthn_credential.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
"time"
)

func WebauthnCredentialToModel(credential *webauthn.Credential, userId uuid.UUID, webauthnUserId uuid.UUID, backupEligible bool, backupState bool) *models.WebauthnCredential {
func WebauthnCredentialToModel(credential *webauthn.Credential, userId string, webauthnUserId uuid.UUID, backupEligible bool, backupState bool) *models.WebauthnCredential {
now := time.Now().UTC()
aaguid, _ := uuid.FromBytes(credential.Authenticator.AAGUID)
credentialID := base64.RawURLEncoding.EncodeToString(credential.ID)
Expand Down
8 changes: 4 additions & 4 deletions server/api/dto/intern/webauthn_session_data.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"github.com/gobuffalo/nulls"
"github.com/gofrs/uuid"
"github.com/teamhanko/passkey-server/persistence/models"
"strings"
"time"
)

Expand All @@ -20,8 +21,8 @@ func WebauthnSessionDataFromModel(data *models.WebauthnSessionData) *webauthn.Se
allowedCredentials = append(allowedCredentials, credentialId)
}
var userId []byte = nil
if !data.UserId.IsNil() {
userId = data.UserId.Bytes()
if strings.TrimSpace(data.UserId) != "" {
userId = []byte(data.UserId)
}
return &webauthn.SessionData{
Challenge: data.Challenge,
Expand All @@ -34,7 +35,6 @@ func WebauthnSessionDataFromModel(data *models.WebauthnSessionData) *webauthn.Se

func WebauthnSessionDataToModel(data *webauthn.SessionData, tenantId uuid.UUID, operation models.Operation) *models.WebauthnSessionData {
id, _ := uuid.NewV4()
userId, _ := uuid.FromBytes(data.UserID)
now := time.Now()

var allowedCredentials []models.WebauthnSessionDataAllowedCredential
Expand All @@ -54,7 +54,7 @@ func WebauthnSessionDataToModel(data *webauthn.SessionData, tenantId uuid.UUID,
return &models.WebauthnSessionData{
ID: id,
Challenge: data.Challenge,
UserId: userId,
UserId: string(data.UserID),
UserVerification: string(data.UserVerification),
CreatedAt: now,
UpdatedAt: now,
Expand Down
5 changes: 2 additions & 3 deletions server/api/dto/intern/webauthn_user.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,11 @@ package intern

import (
"github.com/go-webauthn/webauthn/webauthn"
"github.com/gofrs/uuid"
"github.com/teamhanko/passkey-server/persistence/models"
)

type WebauthnUser struct {
UserId uuid.UUID
UserId string
Name string
Icon string
DisplayName string
Expand All @@ -25,7 +24,7 @@ func NewWebauthnUser(user models.WebauthnUser) *WebauthnUser {
}

func (u *WebauthnUser) WebAuthnID() []byte {
return u.UserId.Bytes()
return []byte(u.UserId)
}

func (u *WebauthnUser) WebAuthnName() string {
Expand Down
4 changes: 2 additions & 2 deletions server/api/dto/request/requests.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ type TenantDto struct {
}

type ListCredentialsDto struct {
UserId string `query:"user_id" validate:"required,uuid4"`
UserId string `query:"user_id" validate:"required"`
}

type DeleteCredentialsDto struct {
Expand All @@ -22,7 +22,7 @@ type UpdateCredentialsDto struct {
}

type InitRegistrationDto struct {
UserId string `json:"user_id" validate:"required,uuid4"`
UserId string `json:"user_id" validate:"required"`
Username string `json:"username" validate:"required"`
DisplayName *string `json:"display_name"`
Icon *string `json:"icon"`
Expand Down
14 changes: 7 additions & 7 deletions server/api/handler/credentials.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ package handler
import (
"fmt"
"github.com/gobuffalo/pop/v6"
"github.com/gofrs/uuid"
"github.com/labstack/echo/v4"
"github.com/teamhanko/passkey-server/api/dto/request"
"github.com/teamhanko/passkey-server/api/dto/response"
"github.com/teamhanko/passkey-server/api/helper"
"github.com/teamhanko/passkey-server/persistence"
"github.com/teamhanko/passkey-server/persistence/models"
"net/http"
Expand Down Expand Up @@ -40,14 +40,14 @@ func (credHandler *credentialsHandler) List(ctx echo.Context) error {
return err
}

userId, err := uuid.FromString(requestDto.UserId)
h, err := helper.GetHandlerContext(ctx)
if err != nil {
ctx.Logger().Error(err)
return err
}

credentialPersister := credHandler.persister.GetWebauthnCredentialPersister(nil)
credentialModels, err := credentialPersister.GetFromUser(userId)
credentialModels, err := credentialPersister.GetFromUser(requestDto.UserId, h.Tenant.ID)
if err != nil {
ctx.Logger().Error(err)
return err
Expand Down Expand Up @@ -83,7 +83,7 @@ func (credHandler *credentialsHandler) Update(ctx echo.Context) error {
})
}

h, err := GetHandlerContext(ctx)
h, err := helper.GetHandlerContext(ctx)
if err != nil {
ctx.Logger().Error(err)
return err
Expand All @@ -98,7 +98,7 @@ func (credHandler *credentialsHandler) Update(ctx echo.Context) error {
ctx.Logger().Error(err)
return err
}
err := h.auditLog.CreateWithConnection(tx, ctx, h.tenant, models.AuditLogWebAuthnCredentialUpdated, &credential.UserId, nil)
err := h.AuditLog.CreateWithConnection(tx, ctx, h.Tenant, models.AuditLogWebAuthnCredentialUpdated, &credential.UserId, nil)
if err != nil {
ctx.Logger().Error(err)
return err
Expand All @@ -121,7 +121,7 @@ func (credHandler *credentialsHandler) Delete(ctx echo.Context) error {
return err
}

h, err := GetHandlerContext(ctx)
h, err := helper.GetHandlerContext(ctx)
if err != nil {
ctx.Logger().Error(err)
return err
Expand All @@ -135,7 +135,7 @@ func (credHandler *credentialsHandler) Delete(ctx echo.Context) error {
return err
}

err = h.auditLog.CreateWithConnection(tx, ctx, h.tenant, models.AuditLogWebAuthnCredentialDeleted, nil, nil)
err = h.AuditLog.CreateWithConnection(tx, ctx, h.Tenant, models.AuditLogWebAuthnCredentialDeleted, nil, nil)
if err != nil {
ctx.Logger().Error(err)
return err
Expand Down
36 changes: 24 additions & 12 deletions server/api/handler/login.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/labstack/echo/v4"
"github.com/teamhanko/passkey-server/api/dto/intern"
"github.com/teamhanko/passkey-server/api/dto/response"
"github.com/teamhanko/passkey-server/api/helper"
"github.com/teamhanko/passkey-server/crypto/jwt"
"github.com/teamhanko/passkey-server/persistence"
"github.com/teamhanko/passkey-server/persistence/models"
Expand All @@ -35,21 +36,21 @@ func NewLoginHandler(persister persistence.Persister) (WebauthnHandler, error) {
}

func (lh *loginHandler) Init(ctx echo.Context) error {
h, err := GetHandlerContext(ctx)
h, err := helper.GetHandlerContext(ctx)
if err != nil {
ctx.Logger().Error(err)
return err
}

options, sessionData, err := h.webauthn.BeginDiscoverableLogin(
webauthn.WithUserVerification(h.config.WebauthnConfig.UserVerification),
options, sessionData, err := h.Webauthn.BeginDiscoverableLogin(
webauthn.WithUserVerification(h.Config.WebauthnConfig.UserVerification),
)
if err != nil {
ctx.Logger().Error(err)
return fmt.Errorf("failed to create webauthn assertion options for discoverable login: %w", err)
}

err = lh.persister.GetWebauthnSessionDataPersister(nil).Create(*intern.WebauthnSessionDataToModel(sessionData, h.tenant.ID, models.WebauthnOperationAuthentication))
err = lh.persister.GetWebauthnSessionDataPersister(nil).Create(*intern.WebauthnSessionDataToModel(sessionData, h.Tenant.ID, models.WebauthnOperationAuthentication))
if err != nil {
ctx.Logger().Error(err)
return fmt.Errorf("failed to store webauthn assertion session data: %w", err)
Expand All @@ -71,7 +72,7 @@ func (lh *loginHandler) Finish(ctx echo.Context) error {
return echo.NewHTTPError(http.StatusBadRequest, err)
}

h, err := GetHandlerContext(ctx)
h, err := helper.GetHandlerContext(ctx)
if err != nil {
ctx.Logger().Error(err)
return err
Expand All @@ -82,20 +83,24 @@ func (lh *loginHandler) Finish(ctx echo.Context) error {
webauthnUserPersister := lh.persister.GetWebauthnUserPersister(tx)
credentialPersister := lh.persister.GetWebauthnCredentialPersister(tx)

sessionData, err := lh.getSessionDataByChallenge(parsedRequest.Response.CollectedClientData.Challenge, sessionDataPersister, h.tenant.ID)
sessionData, err := lh.getSessionDataByChallenge(parsedRequest.Response.CollectedClientData.Challenge, sessionDataPersister, h.Tenant.ID)
if err != nil {
ctx.Logger().Error(err)
return echo.NewHTTPError(http.StatusUnauthorized, "failed to get session data").SetInternal(err)
}
sessionDataModel := intern.WebauthnSessionDataFromModel(sessionData)

webauthnUser, err := lh.getWebauthnUserByUserHandle(parsedRequest.Response.UserHandle, h.tenant.ID, webauthnUserPersister)
webauthnUser, err := lh.getWebauthnUserByUserHandle(parsedRequest.Response.UserHandle, h.Tenant.ID, webauthnUserPersister)
if err != nil {
ctx.Logger().Error(err)
return echo.NewHTTPError(http.StatusUnauthorized, "failed to get user handle").SetInternal(err)
}

credential, err := h.webauthn.ValidateDiscoverableLogin(func(rawID, userHandle []byte) (user webauthn.User, err error) {
// backward compatibility
userId := lh.convertUserHandle(parsedRequest.Response.UserHandle)
parsedRequest.Response.UserHandle = []byte(userId)

credential, err := h.Webauthn.ValidateDiscoverableLogin(func(rawID, userHandle []byte) (user webauthn.User, err error) {
return webauthnUser, nil
}, *sessionDataModel, parsedRequest)

Expand Down Expand Up @@ -154,10 +159,7 @@ func (lh *loginHandler) getSessionDataByChallenge(challenge string, persister pe
}

func (lh *loginHandler) getWebauthnUserByUserHandle(userHandle []byte, tenantId uuid.UUID, persister persisters.WebauthnUserPersister) (*intern.WebauthnUser, error) {
userId, err := uuid.FromBytes(userHandle)
if err != nil {
return nil, echo.NewHTTPError(http.StatusBadRequest, "failed to parse userHandle as uuid").SetInternal(err)
}
userId := lh.convertUserHandle(userHandle)

user, err := persister.GetByUserId(userId, tenantId)
if err != nil {
Expand All @@ -170,3 +172,13 @@ func (lh *loginHandler) getWebauthnUserByUserHandle(userHandle []byte, tenantId

return intern.NewWebauthnUser(*user), nil
}

func (lh *loginHandler) convertUserHandle(userHandle []byte) string {
userId := string(userHandle)
userUuid, err := uuid.FromBytes(userHandle)
if err == nil {
userId = userUuid.String()
}

return userId
}
23 changes: 12 additions & 11 deletions server/api/handler/registration.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/teamhanko/passkey-server/api/dto/intern"
"github.com/teamhanko/passkey-server/api/dto/request"
"github.com/teamhanko/passkey-server/api/dto/response"
"github.com/teamhanko/passkey-server/api/helper"
"github.com/teamhanko/passkey-server/crypto/jwt"
"github.com/teamhanko/passkey-server/persistence"
"github.com/teamhanko/passkey-server/persistence/models"
Expand Down Expand Up @@ -46,7 +47,7 @@ func (r *registrationHandler) Init(ctx echo.Context) error {
return err
}

h, err := GetHandlerContext(ctx)
h, err := helper.GetHandlerContext(ctx)
if err != nil {
ctx.Logger().Error(err)
return err
Expand All @@ -56,7 +57,7 @@ func (r *registrationHandler) Init(ctx echo.Context) error {
webauthnUserPersister := r.persister.GetWebauthnUserPersister(tx)
webauthnSessionPersister := r.persister.GetWebauthnSessionDataPersister(tx)

webauthnUser.Tenant = h.tenant
webauthnUser.Tenant = h.Tenant
internalUserDto, _, err := r.GetWebauthnUser(webauthnUser.UserID, webauthnUser.Tenant.ID, webauthnUserPersister)
if err != nil {
ctx.Logger().Error(err)
Expand All @@ -74,24 +75,24 @@ func (r *registrationHandler) Init(ctx echo.Context) error {
}

t := true
options, sessionData, err := h.webauthn.BeginRegistration(
options, sessionData, err := h.Webauthn.BeginRegistration(
internalUserDto,
webauthn.WithAuthenticatorSelection(protocol.AuthenticatorSelection{
RequireResidentKey: &t,
ResidentKey: protocol.ResidentKeyRequirementRequired,
UserVerification: h.config.WebauthnConfig.UserVerification,
UserVerification: h.Config.WebauthnConfig.UserVerification,
}),
webauthn.WithConveyancePreference(protocol.PreferNoAttestation),
// don't set the excludeCredentials list, so an already registered device can be re-registered
)

err = webauthnSessionPersister.Create(*intern.WebauthnSessionDataToModel(sessionData, h.tenant.ID, models.WebauthnOperationRegistration))
err = webauthnSessionPersister.Create(*intern.WebauthnSessionDataToModel(sessionData, h.Tenant.ID, models.WebauthnOperationRegistration))
if err != nil {
ctx.Logger().Error(err)
return fmt.Errorf("failed to create session data: %w", err)
}

err = h.auditLog.CreateWithConnection(tx, ctx, h.tenant, models.AuditLogWebAuthnRegistrationInitSucceeded, &webauthnUser.UserID, nil)
err = h.AuditLog.CreateWithConnection(tx, ctx, h.Tenant, models.AuditLogWebAuthnRegistrationInitSucceeded, &webauthnUser.UserID, nil)
if err != nil {
ctx.Logger().Error(err)
return fmt.Errorf("failed to create audit log: %w", err)
Expand All @@ -108,7 +109,7 @@ func (r *registrationHandler) Finish(ctx echo.Context) error {
return echo.NewHTTPError(http.StatusBadRequest, "unable to parse credential creation response").SetInternal(err)
}

h, err := GetHandlerContext(ctx)
h, err := helper.GetHandlerContext(ctx)
if err != nil {
ctx.Logger().Error(err)
return err
Expand All @@ -118,13 +119,13 @@ func (r *registrationHandler) Finish(ctx echo.Context) error {
sessionDataPersister := r.persister.GetWebauthnSessionDataPersister(tx)
webauthnUserPersister := r.persister.GetWebauthnUserPersister(tx)

sessionData, err := r.getSessionByChallenge(parsedRequest.Response.CollectedClientData.Challenge, h.tenant.ID, sessionDataPersister)
sessionData, err := r.getSessionByChallenge(parsedRequest.Response.CollectedClientData.Challenge, h.Tenant.ID, sessionDataPersister)
if err != nil {
ctx.Logger().Error(err)
return err
}

webauthnUser, userModel, err := r.GetWebauthnUser(sessionData.UserId, h.tenant.ID, webauthnUserPersister)
webauthnUser, userModel, err := r.GetWebauthnUser(sessionData.UserId, h.Tenant.ID, webauthnUserPersister)
if err != nil {
ctx.Logger().Error(err)
return err
Expand All @@ -134,7 +135,7 @@ func (r *registrationHandler) Finish(ctx echo.Context) error {
return echo.NewHTTPError(http.StatusNotFound, "user not found")
}

credential, err := h.webauthn.CreateCredential(webauthnUser, *intern.WebauthnSessionDataFromModel(sessionData), parsedRequest)
credential, err := h.Webauthn.CreateCredential(webauthnUser, *intern.WebauthnSessionDataFromModel(sessionData), parsedRequest)
if err != nil {
errorMessage := "failed to validate attestation"
errorStatus := http.StatusBadRequest
Expand Down Expand Up @@ -193,7 +194,7 @@ func (r *registrationHandler) getSessionByChallenge(challenge string, tenantId u
return sessionData, nil
}

func (r *registrationHandler) GetWebauthnUser(userId uuid.UUID, tenantId uuid.UUID, persister persisters.WebauthnUserPersister) (*intern.WebauthnUser, *models.WebauthnUser, error) {
func (r *registrationHandler) GetWebauthnUser(userId string, tenantId uuid.UUID, persister persisters.WebauthnUserPersister) (*intern.WebauthnUser, *models.WebauthnUser, error) {
user, err := persister.GetByUserId(userId, tenantId)
if err != nil {
return nil, nil, err
Expand Down
Loading