-
Notifications
You must be signed in to change notification settings - Fork 4
/
authenticator.go
124 lines (105 loc) · 3.75 KB
/
authenticator.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
// SPDX-FileCopyrightText: 2024 Comcast Cable Communications Management, LLC
// SPDX-License-Identifier: Apache-2.0
package bascule
import "context"
// AuthenticateEvent represents the result of bascule's authenticate workflow.
type AuthenticateEvent[S any] struct {
// Source is the object that was parsed to produce the token.
// This field is always set.
Source S
// Token is the token that resulted from parsing the source. This field
// will only be set if parsing was successful.
Token Token
// Err is the error that resulted from authentication. This field will be
// nil for a successful authentication.
Err error
}
// AuthenticatorOption is a configurable option for an Authenticator.
type AuthenticatorOption[S any] interface {
apply(*Authenticator[S]) error
}
type authenticatorOptionFunc[S any] func(*Authenticator[S]) error
//nolint:unused
func (aof authenticatorOptionFunc[S]) apply(a *Authenticator[S]) error { return aof(a) }
// WithAuthenticateListeners adds listeners to the Authenticator being built.
// Multiple calls for this option are cumulative.
func WithAuthenticateListeners[S any](more ...Listener[AuthenticateEvent[S]]) AuthenticatorOption[S] {
return authenticatorOptionFunc[S](
func(a *Authenticator[S]) error {
a.listeners = a.listeners.Append(more...)
return nil
},
)
}
// WithAuthenticateListenerFuncs is a closure variant of WithAuthenticateListeners.
func WithAuthenticateListenerFuncs[S any](more ...ListenerFunc[AuthenticateEvent[S]]) AuthenticatorOption[S] {
return authenticatorOptionFunc[S](
func(a *Authenticator[S]) error {
a.listeners = a.listeners.AppendFunc(more...)
return nil
},
)
}
// WithTokenParsers adds token parsers to the Authenticator being built.
// Multiple calls for this option are cumulative.
func WithTokenParsers[S any](more ...TokenParser[S]) AuthenticatorOption[S] {
return authenticatorOptionFunc[S](
func(a *Authenticator[S]) error {
a.parsers = a.parsers.Append(more...)
return nil
},
)
}
// WithValidators adds validators to the Authenticator being built.
// Multiple calls for this option are cumulative.
func WithValidators[S any](more ...Validator[S]) AuthenticatorOption[S] {
return authenticatorOptionFunc[S](
func(a *Authenticator[S]) error {
a.validators = a.validators.Append(more...)
return nil
},
)
}
// NewAuthenticator constructs an Authenticator workflow using the supplied options.
//
// At least (1) token parser must be supplied in the options, or this
// function returns ErrNoTokenParsers.
func NewAuthenticator[S any](opts ...AuthenticatorOption[S]) (a *Authenticator[S], err error) {
a = new(Authenticator[S])
for i := 0; err == nil && i < len(opts); i++ {
err = opts[i].apply(a)
}
if a.parsers.Len() == 0 {
return nil, ErrNoTokenParsers
}
return
}
// Authenticator provides bascule's authentication workflow. This type handles
// parsing tokens, validating them, and dispatching authentication events to listeners.
type Authenticator[S any] struct {
listeners Listeners[AuthenticateEvent[S]]
parsers TokenParsers[S]
validators Validators[S]
}
// Authenticate implements bascule's authentication pipeline. The following steps are
// performed:
//
// (1) The token is extracted from the source using the configured parser(s)
// (2) The token is validated using any configured validator(s)
// (3) Appropriate events are dispatched to listeners after either of steps (1) or (2)
func (a *Authenticator[S]) Authenticate(ctx context.Context, source S) (token Token, err error) {
token, err = a.parsers.Parse(ctx, source)
if err == nil {
var next Token
next, err = a.validators.Validate(ctx, source, token)
if next != nil {
token = next
}
}
a.listeners.OnEvent(AuthenticateEvent[S]{
Source: source,
Token: token,
Err: err,
})
return
}