✨ Added Generate use case in token package
This commit is contained in:
parent
d5606340a0
commit
f99cd0955d
|
@ -7,9 +7,24 @@ import (
|
|||
"source.toby3d.me/website/oauth/internal/domain"
|
||||
)
|
||||
|
||||
type UseCase interface {
|
||||
Verify(ctx context.Context, accessToken string) (*domain.Token, error)
|
||||
Revoke(ctx context.Context, accessToken string) error
|
||||
}
|
||||
type (
|
||||
GenerateOptions struct {
|
||||
ClientID string
|
||||
Me string
|
||||
Scopes []string
|
||||
NonceLength int
|
||||
}
|
||||
|
||||
UseCase interface {
|
||||
// Generate generates a new Token based on the session data.
|
||||
Generate(ctx context.Context, opts GenerateOptions) (*domain.Token, error)
|
||||
|
||||
// Verify checks the AccessToken and returns the associated information.
|
||||
Verify(ctx context.Context, accessToken string) (*domain.Token, error)
|
||||
|
||||
// Revoke revokes the AccessToken and blocks its further use.
|
||||
Revoke(ctx context.Context, accessToken string) error
|
||||
}
|
||||
)
|
||||
|
||||
var ErrRevoke = errors.New("this token has been revoked")
|
||||
|
|
|
@ -3,6 +3,7 @@ package usecase
|
|||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/lestrrat-go/jwx/jwa"
|
||||
"github.com/lestrrat-go/jwx/jwt"
|
||||
|
@ -11,19 +12,60 @@ import (
|
|||
|
||||
"source.toby3d.me/website/oauth/internal/config"
|
||||
"source.toby3d.me/website/oauth/internal/domain"
|
||||
"source.toby3d.me/website/oauth/internal/random"
|
||||
"source.toby3d.me/website/oauth/internal/token"
|
||||
)
|
||||
|
||||
type tokenUseCase struct {
|
||||
tokens token.Repository
|
||||
configer config.UseCase
|
||||
type (
|
||||
Config struct {
|
||||
Configer config.UseCase
|
||||
Tokens token.Repository
|
||||
}
|
||||
|
||||
tokenUseCase struct {
|
||||
configer config.UseCase
|
||||
tokens token.Repository
|
||||
}
|
||||
)
|
||||
|
||||
func NewTokenUseCase(config Config) token.UseCase {
|
||||
return &tokenUseCase{
|
||||
configer: config.Configer,
|
||||
tokens: config.Tokens,
|
||||
}
|
||||
}
|
||||
|
||||
func NewTokenUseCase(tokens token.Repository, configer config.UseCase) token.UseCase {
|
||||
return &tokenUseCase{
|
||||
tokens: tokens,
|
||||
configer: configer,
|
||||
// Generate generates a new Token based on the session data.
|
||||
func (useCase *tokenUseCase) Generate(ctx context.Context, opts token.GenerateOptions) (*domain.Token, error) {
|
||||
nonce, err := random.String(opts.NonceLength)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "cannot generate code")
|
||||
}
|
||||
|
||||
t := jwt.New()
|
||||
now := time.Now().UTC().Round(time.Second)
|
||||
|
||||
t.Set(jwt.IssuerKey, opts.ClientID)
|
||||
t.Set(jwt.SubjectKey, opts.Me)
|
||||
t.Set(jwt.ExpirationKey, now.Add(useCase.configer.GetIndieAuthAccessTokenExpirationTime()))
|
||||
t.Set(jwt.NotBeforeKey, now)
|
||||
t.Set(jwt.IssuedAtKey, now)
|
||||
t.Set("scope", strings.Join(opts.Scopes, " "))
|
||||
t.Set("nonce", nonce)
|
||||
|
||||
token, err := jwt.Sign(t,
|
||||
jwa.SignatureAlgorithm(useCase.configer.GetIndieAuthJWTSigningAlgorithm()),
|
||||
[]byte(useCase.configer.GetIndieAuthJWTSecret()))
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "cannot sign a new access token")
|
||||
}
|
||||
|
||||
return &domain.Token{
|
||||
Scopes: opts.Scopes,
|
||||
AccessToken: string(token),
|
||||
ClientID: opts.ClientID,
|
||||
Me: opts.Me,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (useCase *tokenUseCase) Verify(ctx context.Context, accessToken string) (*domain.Token, error) {
|
||||
|
|
|
@ -2,10 +2,11 @@ package usecase_test
|
|||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
"github.com/lestrrat-go/jwx/jwt"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
|
@ -17,12 +18,38 @@ import (
|
|||
ucase "source.toby3d.me/website/oauth/internal/token/usecase"
|
||||
)
|
||||
|
||||
func TestVerify(t *testing.T) {
|
||||
func TestGenerate(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
v := viper.New()
|
||||
v.Set("indieauth.jwtSigningAlgorithm", "HS256")
|
||||
v.Set("indieauth.jwtSecret", "hackme")
|
||||
configer := configucase.NewConfigUseCase(configrepo.NewViperConfigRepository(domain.TestConfig(t)))
|
||||
options := token.GenerateOptions{
|
||||
ClientID: "https://app.example.com/",
|
||||
Me: "https://user.example.net/",
|
||||
Scopes: []string{"create", "update", "delete"},
|
||||
NonceLength: 42,
|
||||
}
|
||||
|
||||
result, err := ucase.NewTokenUseCase(ucase.Config{
|
||||
Configer: configer,
|
||||
Tokens: nil,
|
||||
}).Generate(context.TODO(), options)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, options.ClientID, result.ClientID)
|
||||
assert.Equal(t, options.Me, result.Me)
|
||||
assert.Equal(t, options.Scopes, result.Scopes)
|
||||
|
||||
token, err := jwt.ParseString(result.AccessToken)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, options.Me, token.Subject())
|
||||
assert.Equal(t, options.ClientID, token.Issuer())
|
||||
|
||||
scope, ok := token.Get("scope")
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, strings.Join(options.Scopes, " "), scope)
|
||||
}
|
||||
|
||||
func TestVerify(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
repo := repository.NewMemoryTokenRepository(new(sync.Map))
|
||||
useCase := ucase.NewTokenUseCase(repo, configucase.NewConfigUseCase(configrepo.NewViperConfigRepository(v)))
|
||||
|
|
Loading…
Reference in New Issue