This commit is contained in:
parent
5232bb0741
commit
5d1473864b
1 changed files with 104 additions and 2 deletions
|
@ -15,6 +15,13 @@ import (
|
||||||
"git.mstar.dev/mstar/linstrom/storage-new/models"
|
"git.mstar.dev/mstar/linstrom/storage-new/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Helper for discovering a user during a dicovering login (user not known yet).
|
||||||
|
// Not thread safe and should only be used once.
|
||||||
|
// FoundUser will be set after a successful call to userWithPasskeyDiscoverer
|
||||||
|
type passkeyDiscoverer struct {
|
||||||
|
FoundUser *models.User
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Check if passkey encryption is viable
|
// TODO: Check if passkey encryption is viable
|
||||||
// Check if encryption for passkey info data is viable to implement
|
// Check if encryption for passkey info data is viable to implement
|
||||||
// and if we should do it.
|
// and if we should do it.
|
||||||
|
@ -25,7 +32,7 @@ import (
|
||||||
|
|
||||||
// Start the login process via passkey for a given username.
|
// Start the login process via passkey for a given username.
|
||||||
// Returns the credential options the passkey needs to sign
|
// Returns the credential options the passkey needs to sign
|
||||||
func (a *Authenticator) StartPasskeyLogin(
|
func (a *Authenticator) StartPasskeyLoginWithUsername(
|
||||||
username string,
|
username string,
|
||||||
) (*protocol.CredentialAssertion, string, error) {
|
) (*protocol.CredentialAssertion, string, error) {
|
||||||
if ok, err := a.canUsernameLogin(username); !ok {
|
if ok, err := a.canUsernameLogin(username); !ok {
|
||||||
|
@ -58,7 +65,7 @@ func (a *Authenticator) StartPasskeyLogin(
|
||||||
|
|
||||||
// Complete a passkey login request
|
// Complete a passkey login request
|
||||||
// Takes the username logging in as well as the raw request containing the passkey response
|
// Takes the username logging in as well as the raw request containing the passkey response
|
||||||
func (a *Authenticator) CompletePasskeyLogin(
|
func (a *Authenticator) CompletePasskeyLoginWithUsername(
|
||||||
username string,
|
username string,
|
||||||
sessionId string,
|
sessionId string,
|
||||||
response *http.Request,
|
response *http.Request,
|
||||||
|
@ -132,6 +139,82 @@ func (a *Authenticator) CompletePasskeyLogin(
|
||||||
return dbAccessToken.Token, nil
|
return dbAccessToken.Token, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Start the login process via passkey for an unknown username.
|
||||||
|
// Returns the credential options the passkey needs to sign.
|
||||||
|
// The relevant user will be discovered during the completion stage
|
||||||
|
func (a *Authenticator) StartPasskeyLoginDiscovery() (*protocol.CredentialAssertion, string, error) {
|
||||||
|
panic("not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Complete a passkey login request for an unknown user.
|
||||||
|
func (a *Authenticator) CompletePasskeyLoginDiscovery(
|
||||||
|
sessionId string,
|
||||||
|
response *http.Request,
|
||||||
|
) (accessToken string, err error) {
|
||||||
|
// Get user in question
|
||||||
|
// Get latest login token data
|
||||||
|
loginToken, err := dbgen.LoginProcessToken.Where(dbgen.LoginProcessToken.Token.Eq(sessionId)).
|
||||||
|
First()
|
||||||
|
if err != nil {
|
||||||
|
return "", other.Error(
|
||||||
|
"auth",
|
||||||
|
"failed to get user's login token for passkey login completion",
|
||||||
|
err,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
// Check if that token has expired
|
||||||
|
if loginToken.ExpiresAt.Before(time.Now()) {
|
||||||
|
return "", ErrProcessTimeout
|
||||||
|
}
|
||||||
|
var pkeySession webauthn.SessionData
|
||||||
|
err = json.Unmarshal([]byte(loginToken.Name), &pkeySession)
|
||||||
|
if err != nil {
|
||||||
|
return "", other.Error("auth", "failed to unmarshal passkey session for user", err)
|
||||||
|
}
|
||||||
|
discoverer := a.getPasskeyDiscoverer()
|
||||||
|
// Hand data to webauthn for completion
|
||||||
|
newSession, err := a.webauthn.FinishDiscoverableLogin(
|
||||||
|
discoverer.userWithPasskeyDiscoverer,
|
||||||
|
pkeySession,
|
||||||
|
response,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return "", other.Error("auth", "passkey completion failed", err)
|
||||||
|
}
|
||||||
|
// TODO: Utilise clone warning
|
||||||
|
// newSession.Authenticator.CloneWarning
|
||||||
|
|
||||||
|
jsonSessionId, err := json.Marshal(newSession.ID)
|
||||||
|
if err != nil {
|
||||||
|
return "", other.Error("auth", "failed to marshal session", err)
|
||||||
|
}
|
||||||
|
jsonSession, err := json.Marshal(newSession.ID)
|
||||||
|
if err != nil {
|
||||||
|
return "", other.Error("auth", "failed to marshal session", err)
|
||||||
|
}
|
||||||
|
// Update credentials
|
||||||
|
// WARN: I am not sure if this will work
|
||||||
|
// Using the ID of the passkey session *should* be unique enough to identify the correct one
|
||||||
|
// Of course, even then, there's still the problem of matching as
|
||||||
|
// I can't yet guarantee that the parsed json content for the ID would be the same
|
||||||
|
_, err = dbgen.UserAuthMethod.Where(dbgen.UserAuthMethod.Token.Like("%"+string(jsonSessionId)+"%")).
|
||||||
|
Update(dbgen.UserAuthMethod.Token, jsonSession)
|
||||||
|
if err != nil {
|
||||||
|
return "", other.Error("auth", "failed to update credentials", err)
|
||||||
|
}
|
||||||
|
dbAccessToken := models.AccessToken{
|
||||||
|
User: *discoverer.FoundUser,
|
||||||
|
UserId: discoverer.FoundUser.ID,
|
||||||
|
ExpiresAt: calcAccessExpirationTimestamp(),
|
||||||
|
}
|
||||||
|
err = dbgen.AccessToken.Omit(dbgen.AccessToken.Token).Create(&dbAccessToken)
|
||||||
|
if err != nil {
|
||||||
|
return "", other.Error("auth", "failed to generate access token", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return dbAccessToken.Token, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Start the process of registrating a passkey to an account
|
// Start the process of registrating a passkey to an account
|
||||||
func (a *Authenticator) StartPasskeyRegistration(
|
func (a *Authenticator) StartPasskeyRegistration(
|
||||||
username string,
|
username string,
|
||||||
|
@ -219,3 +302,22 @@ func (a *Authenticator) CompletePasskeyRegistration(
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get a new passkey discoverer.
|
||||||
|
func (a *Authenticator) getPasskeyDiscoverer() *passkeyDiscoverer {
|
||||||
|
// nothing special yet, might use session id for further verification
|
||||||
|
return &passkeyDiscoverer{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// userWithPasskeyDiscoverer implements webauthn.DiscoverableUserHandler
|
||||||
|
// for the use during a discovering login process
|
||||||
|
func (d *passkeyDiscoverer) userWithPasskeyDiscoverer(
|
||||||
|
rawID, userHandle []byte,
|
||||||
|
) (user webauthn.User, err error) {
|
||||||
|
dbUser, err := dbgen.User.Where(dbgen.User.PasskeyId.Eq(userHandle)).First()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
d.FoundUser = dbUser
|
||||||
|
return &fakeUser{dbUser}, nil
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue