auth/internal/auth/usecase/auth_ucase.go

84 lines
2.5 KiB
Go

package usecase
import (
"context"
"fmt"
"source.toby3d.me/website/indieauth/internal/auth"
"source.toby3d.me/website/indieauth/internal/domain"
"source.toby3d.me/website/indieauth/internal/profile"
"source.toby3d.me/website/indieauth/internal/random"
"source.toby3d.me/website/indieauth/internal/session"
)
type authUseCase struct {
config *domain.Config
profiles profile.Repository
sessions session.Repository
}
// NewAuthUseCase creates a new authentication use case.
func NewAuthUseCase(sessions session.Repository, profiles profile.Repository, config *domain.Config) auth.UseCase {
return &authUseCase{
config: config,
profiles: profiles,
sessions: sessions,
}
}
func (useCase *authUseCase) Generate(ctx context.Context, opts auth.GenerateOptions) (string, error) {
code, err := random.String(useCase.config.Code.Length)
if err != nil {
return "", fmt.Errorf("cannot generate random code: %w", err)
}
if err = useCase.sessions.Create(ctx, &domain.Session{
ClientID: opts.ClientID,
Code: code,
CodeChallenge: opts.CodeChallenge,
CodeChallengeMethod: opts.CodeChallengeMethod,
Me: opts.Me,
RedirectURI: opts.RedirectURI,
Scope: opts.Scope,
}); err != nil {
return "", fmt.Errorf("cannot save session in store: %w", err)
}
return code, nil
}
func (useCase *authUseCase) Exchange(ctx context.Context, opts auth.ExchangeOptions) (*domain.Me, error) {
session, err := useCase.sessions.GetAndDelete(ctx, opts.Code)
if err != nil {
return nil, fmt.Errorf("cannot find session in store: %w", err)
}
if opts.ClientID.String() != session.ClientID.String() {
return nil, domain.NewError(
domain.ErrorCodeInvalidRequest,
"client's URL MUST match the client_id used in the authentication request",
"https://indieauth.net/source/#request",
)
}
if opts.RedirectURI.String() != session.RedirectURI.String() {
return nil, domain.NewError(
domain.ErrorCodeInvalidRequest,
"client's redirect URL MUST match the initial authentication request",
"https://indieauth.net/source/#request",
)
}
if session.CodeChallenge != "" &&
!session.CodeChallengeMethod.Validate(session.CodeChallenge, opts.CodeVerifier) {
return nil, domain.NewError(
domain.ErrorCodeInvalidRequest,
"code_verifier is not hashes to the same value as given in the code_challenge in the original "+
"authorization request",
"https://indieauth.net/source/#request",
)
}
return session.Me, nil
}