authkit initial
This commit is contained in:
parent
5173b0a43d
commit
134393fbca
43 changed files with 5188 additions and 1 deletions
62
service_magic.go
Normal file
62
service_magic.go
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
package authkit
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"git.juancwu.dev/juancwu/errx"
|
||||
)
|
||||
|
||||
// RequestMagicLink mints a single-use magic-link token for the email and
|
||||
// returns the plaintext for delivery. ErrUserNotFound is returned for
|
||||
// unregistered emails.
|
||||
func (a *Auth) RequestMagicLink(ctx context.Context, email string) (string, error) {
|
||||
const op = "authkit.Auth.RequestMagicLink"
|
||||
u, err := a.deps.Users.GetUserByEmail(ctx, normalizeEmail(email))
|
||||
if err != nil {
|
||||
return "", errx.Wrap(op, err)
|
||||
}
|
||||
plaintext, hash, err := mintSecret(prefixMagicLink, a.cfg.Random)
|
||||
if err != nil {
|
||||
return "", errx.Wrap(op, err)
|
||||
}
|
||||
now := a.now()
|
||||
t := &Token{
|
||||
Hash: hash,
|
||||
Kind: TokenMagicLink,
|
||||
UserID: u.ID,
|
||||
CreatedAt: now,
|
||||
ExpiresAt: now.Add(a.cfg.MagicLinkTTL),
|
||||
}
|
||||
if err := a.deps.Tokens.CreateToken(ctx, t); err != nil {
|
||||
return "", errx.Wrap(op, err)
|
||||
}
|
||||
return plaintext, nil
|
||||
}
|
||||
|
||||
// ConsumeMagicLink consumes the magic-link token and returns the
|
||||
// authenticated user. Callers typically follow this with IssueSession or
|
||||
// IssueJWT to actually log the user in.
|
||||
func (a *Auth) ConsumeMagicLink(ctx context.Context, plaintextToken string) (*User, error) {
|
||||
const op = "authkit.Auth.ConsumeMagicLink"
|
||||
hash, ok := parseSecret(prefixMagicLink, plaintextToken)
|
||||
if !ok {
|
||||
return nil, errx.Wrap(op, ErrTokenInvalid)
|
||||
}
|
||||
now := a.now()
|
||||
t, err := a.deps.Tokens.ConsumeToken(ctx, TokenMagicLink, hash, now)
|
||||
if err != nil {
|
||||
return nil, errx.Wrap(op, err)
|
||||
}
|
||||
u, err := a.deps.Users.GetUserByID(ctx, t.UserID)
|
||||
if err != nil {
|
||||
return nil, errx.Wrap(op, err)
|
||||
}
|
||||
// A successful magic-link login also implicitly verifies the email
|
||||
// (the user demonstrably controls the inbox).
|
||||
if u.EmailVerifiedAt == nil {
|
||||
if err := a.deps.Users.SetEmailVerified(ctx, u.ID, now); err == nil {
|
||||
u.EmailVerifiedAt = &now
|
||||
}
|
||||
}
|
||||
return u, nil
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue