Skip to content

Commit 3ee1a53

Browse files
author
modoulo boly sow
committed
auth: add bearer token support and domain validation
Signed-off-by: modoulo boly sow <[email protected]>
1 parent 3d6bd1c commit 3ee1a53

File tree

3 files changed

+95
-13
lines changed

3 files changed

+95
-13
lines changed

go.mod

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
module github.com/projectcontour/contour-authserver
22

3-
go 1.23
3+
go 1.23.0
4+
45
toolchain go1.23.2
56

67
require (

pkg/auth/oidc_connect.go

Lines changed: 90 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -88,18 +88,29 @@ func (o *OIDCConnect) isValidState(ctx context.Context, req *Request, url *url.U
8888
// Do we have stateid stored in querystring
8989
var state *store.OIDCState
9090

91-
stateToken := url.Query().Get(stateQueryParamName)
92-
93-
stateByte, err := o.Cache.Get(stateToken)
94-
if err == nil {
95-
state = store.ConvertToType(stateByte)
96-
}
91+
// Check if there's a bearer token in the Authorization header
92+
authHeader := req.Request.Header.Get("Authorization")
93+
if strings.HasPrefix(authHeader, "Bearer ") {
94+
// Extract the token
95+
token := strings.TrimPrefix(authHeader, "Bearer ")
96+
// Create new state with the token
97+
state = store.NewState()
98+
state.Status = store.StatusTokenReady
99+
state.IDToken = token
100+
} else {
101+
stateToken := url.Query().Get(stateQueryParamName)
97102

98-
// State not found, try to retrieve from cookies.
99-
if state == nil {
100-
state, _ = o.getStateFromCookie(req)
103+
stateByte, err := o.Cache.Get(stateToken)
104+
if err == nil {
105+
state = store.ConvertToType(stateByte)
106+
}
107+
// State not found, try to retrieve from cookies.
108+
if state == nil {
109+
state, _ = o.getStateFromCookie(req)
110+
}
101111
}
102112

113+
// State exists, proceed with token validation.
103114
// State exists, proceed with token validation.
104115
if state != nil {
105116
// Re-initialize provider to refresh the context, this seems like bugs with coreos go-oidc module.
@@ -116,7 +127,7 @@ func (o *OIDCConnect) isValidState(ctx context.Context, req *Request, url *url.U
116127

117128
resp.Response.Header.Add(oauthTokenName, string(stateJSON))
118129

119-
if err := o.Cache.Delete(state.OAuthState); err != nil {
130+
if err := o.Cache.Delete(state.OAuthState); err != nil && err != bigcache.ErrEntryNotFound {
120131
o.Log.Error(err, "error deleting state")
121132
}
122133

@@ -135,6 +146,13 @@ func (o *OIDCConnect) loginHandler(u *url.URL) Response {
135146
state.RequestPath = path.Join(u.Host, u.Path)
136147
state.Scheme = u.Scheme
137148

149+
config := o.oauth2Config()
150+
151+
redirectURL := fmt.Sprintf("%s://%s%s", u.Scheme, u.Host, u.Path)
152+
if redirectURL != config.RedirectURL && matchDomain(redirectURL, o.OidcConfig.AuthorizedRedirectDomains) {
153+
config.RedirectURL = redirectURL
154+
}
155+
138156
authCodeURL := o.oauth2Config().AuthCodeURL(state.OAuthState)
139157

140158
byteState := store.ConvertToByte(state)
@@ -205,6 +223,9 @@ func (o *OIDCConnect) callbackHandler(ctx context.Context, u *url.URL) (Response
205223
resp.Response.Header.Add("Location",
206224
fmt.Sprintf("%s://%s?%s=%s", state.Scheme, state.RequestPath, stateQueryParamName, state.OAuthState))
207225

226+
stateJSON, _ := json.Marshal(state)
227+
resp.Response.Header.Add("Set-Cookie", fmt.Sprintf("%s=%s; Path=/; Secure; SameSite=Lax", oauthTokenName, string(stateJSON)))
228+
208229
return resp, nil
209230
}
210231

@@ -246,7 +267,6 @@ func (o *OIDCConnect) getStateFromCookie(req *Request) (*store.OIDCState, error)
246267
// Check through and get the right cookies
247268
if len(cookieVal) > 0 {
248269
cookies := strings.Split(cookieVal, ";")
249-
250270
for _, c := range cookies {
251271
c = strings.TrimSpace(c)
252272
if strings.HasPrefix(c, oauthTokenName) {
@@ -280,7 +300,8 @@ func (o *OIDCConnect) oauth2Config() *oauth2.Config {
280300
ClientSecret: o.OidcConfig.ClientSecret,
281301
Endpoint: o.provider.Endpoint(),
282302
Scopes: o.OidcConfig.Scopes,
283-
RedirectURL: o.OidcConfig.RedirectURL + o.OidcConfig.RedirectPath,
303+
304+
RedirectURL: o.OidcConfig.RedirectURL + o.OidcConfig.RedirectPath,
284305
}
285306
}
286307

@@ -313,3 +334,60 @@ func parseURL(req *Request) *url.URL {
313334

314335
return u
315336
}
337+
338+
// matchDomain checks if a domain matches any of the allowed patterns.
339+
func matchDomain(domain string, allowedPatterns []string) bool {
340+
for _, pattern := range allowedPatterns {
341+
if matchPattern(domain, pattern) {
342+
return true
343+
}
344+
}
345+
return false
346+
}
347+
348+
// matchPattern checks if a domain matches a single pattern with wildcards.
349+
func matchPattern(domain, pattern string) bool {
350+
// Split the pattern and domain into parts.
351+
patternParts := strings.Split(pattern, ".")
352+
domainParts := strings.Split(domain, ".")
353+
354+
// If the number of parts doesn't match, it's not a match.
355+
if len(patternParts) != len(domainParts) {
356+
return false
357+
}
358+
359+
// Check each part of the pattern against the domain.
360+
for i := range patternParts {
361+
if !matchPart(domainParts[i], patternParts[i]) {
362+
return false
363+
}
364+
}
365+
366+
return true
367+
}
368+
369+
// matchPart checks if a single part of the domain matches the pattern part.
370+
func matchPart(domainPart, patternPart string) bool {
371+
// If the pattern part is a wildcard, it matches anything.
372+
if patternPart == "*" {
373+
return true
374+
}
375+
376+
// Split the pattern part by the wildcard.
377+
parts := strings.Split(patternPart, "*")
378+
379+
// Check if the domain part matches the pattern parts in sequence.
380+
pos := 0
381+
for _, part := range parts {
382+
if part == "" {
383+
continue
384+
}
385+
index := strings.Index(domainPart[pos:], part)
386+
if index == -1 {
387+
return false
388+
}
389+
pos += index + len(part)
390+
}
391+
392+
return true
393+
}

pkg/config/config.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@ type OIDCConfig struct {
4242

4343
// T decide wether should this be use
4444
SessionSecurityKey string `yaml:"sessionSecurityKey" envconfig:"SESSION_SECURITY_KEY"`
45+
46+
// AuthorizedRedirectDomains is the domain of the resource server.
47+
AuthorizedRedirectDomains []string `yaml:"authorizedRedirectDomains"`
4548
}
4649

4750
// NewConfig returns a Config struct from serialized config file.

0 commit comments

Comments
 (0)