auth/internal/auth/usecase/auth_ucase.go

86 lines
2.6 KiB
Go
Raw Normal View History

2022-01-13 20:49:41 +00:00
package usecase
import (
"context"
"fmt"
"source.toby3d.me/toby3d/auth/internal/auth"
"source.toby3d.me/toby3d/auth/internal/domain"
"source.toby3d.me/toby3d/auth/internal/profile"
"source.toby3d.me/toby3d/auth/internal/random"
"source.toby3d.me/toby3d/auth/internal/session"
2022-01-13 20:49:41 +00:00
)
type authUseCase struct {
config *domain.Config
sessions session.Repository
2022-02-17 16:10:52 +00:00
profiles profile.Repository
2022-01-13 20:49:41 +00:00
}
// NewAuthUseCase creates a new authentication use case.
2022-02-17 16:10:52 +00:00
func NewAuthUseCase(sessions session.Repository, profiles profile.Repository, config *domain.Config) auth.UseCase {
2022-01-13 20:49:41 +00:00
return &authUseCase{
config: config,
sessions: sessions,
2022-02-17 16:10:52 +00:00
profiles: profiles,
2022-01-13 20:49:41 +00:00
}
}
2022-02-17 16:10:52 +00:00
func (uc *authUseCase) Generate(ctx context.Context, opts auth.GenerateOptions) (string, error) {
code, err := random.String(uc.config.Code.Length)
2022-01-13 20:49:41 +00:00
if err != nil {
return "", fmt.Errorf("cannot generate random code: %w", err)
}
2022-02-17 16:10:52 +00:00
var userInfo *domain.Profile
// NOTE(toby3d): We request information about the profile only if there
// is a corresponding Scope. However, the availability of this
// information in the token is not guaranteed and is completely optional:
// https://indieauth.net/source/#profile-information
if opts.Scope.Has(domain.ScopeProfile) {
2022-02-17 21:53:39 +00:00
if userInfo, err = uc.profiles.Get(ctx, opts.Me); err == nil &&
userInfo.Email != nil && !opts.Scope.Has(domain.ScopeEmail) {
2022-02-17 16:10:52 +00:00
userInfo.Email = nil
}
}
if err = uc.sessions.Create(ctx, &domain.Session{
2022-01-13 20:49:41 +00:00
ClientID: opts.ClientID,
Code: code,
CodeChallenge: opts.CodeChallenge,
CodeChallengeMethod: opts.CodeChallengeMethod,
Me: opts.Me,
2022-02-17 16:10:52 +00:00
Profile: userInfo,
2022-01-13 20:49:41 +00:00
RedirectURI: opts.RedirectURI,
Scope: opts.Scope,
}); err != nil {
return "", fmt.Errorf("cannot save session in store: %w", err)
}
return code, nil
}
2022-02-17 16:10:52 +00:00
func (uc *authUseCase) Exchange(ctx context.Context, opts auth.ExchangeOptions) (*domain.Me, *domain.Profile, error) {
session, err := uc.sessions.GetAndDelete(ctx, opts.Code)
2022-01-13 20:49:41 +00:00
if err != nil {
2022-02-17 16:10:52 +00:00
return nil, nil, fmt.Errorf("cannot find session in store: %w", err)
2022-01-13 20:49:41 +00:00
}
if opts.ClientID.String() != session.ClientID.String() {
2022-02-17 16:10:52 +00:00
return nil, nil, auth.ErrMismatchClientID
2022-01-13 20:49:41 +00:00
}
if opts.RedirectURI.String() != session.RedirectURI.String() {
2022-02-17 16:10:52 +00:00
return nil, nil, auth.ErrMismatchRedirectURI
2022-01-13 20:49:41 +00:00
}
2022-02-17 16:10:52 +00:00
if session.CodeChallenge != "" &&
session.CodeChallengeMethod != domain.CodeChallengeMethodUndefined &&
2022-01-13 20:49:41 +00:00
!session.CodeChallengeMethod.Validate(session.CodeChallenge, opts.CodeVerifier) {
2022-02-17 16:10:52 +00:00
return nil, nil, auth.ErrMismatchPKCE
2022-01-13 20:49:41 +00:00
}
2022-02-17 16:10:52 +00:00
return session.Me, session.Profile, nil
2022-01-13 20:49:41 +00:00
}