This repository has been archived by the owner on Oct 23, 2024. It is now read-only.
forked from arrikto/oidc-authservice
-
Notifications
You must be signed in to change notification settings - Fork 0
/
authenticator_session.go
99 lines (89 loc) · 3.33 KB
/
authenticator_session.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
package main
import (
"net/http"
"net/http/httptest"
oidc "github.com/coreos/go-oidc"
"github.com/gorilla/sessions"
"github.com/pkg/errors"
"golang.org/x/oauth2"
"k8s.io/apiserver/pkg/authentication/authenticator"
"k8s.io/apiserver/pkg/authentication/user"
)
type sessionAuthenticator struct {
// store is the session store.
store sessions.Store
// cookie is the name of the cookie that holds the session value.
cookie string
// header is the header to check as an alternative to finding the session
// value.
header string
// strictSessionValidation mode checks the validity of the access token
// connected with the session on every request.
strictSessionValidation bool
// caBundle specifies CAs to trust when talking with the OIDC Provider.
// Relevant only when strictSessionValidation is enabled.
caBundle []byte
// oauth2Config is the config to use when talking with the OIDC Provider.
// Relevant only when strictSessionValidation is enabled.
oauth2Config *oauth2.Config
// provider is the OIDC Provider.
// Relevant only when strictSessionValidation is enabled.
provider *oidc.Provider
}
func (sa *sessionAuthenticator) AuthenticateRequest(r *http.Request) (*authenticator.Response, bool, error) {
logger := loggerForRequest(r, "session authenticator")
// Get session from header or cookie
session, authMethod, err := sessionFromRequest(r, sa.store, sa.cookie, sa.header)
// Check if user session is valid
if err != nil {
return nil, false, errors.Wrap(err, "couldn't get user session")
}
if session.IsNew {
logger.Info("Failed to retrieve a valid session")
return nil, false, nil
}
// User is logged in
if sa.strictSessionValidation {
ctx := setTLSContext(r.Context(), sa.caBundle)
token := session.Values[userSessionOAuth2Tokens].(oauth2.Token)
// TokenSource takes care of automatically renewing the access token.
_, err := GetUserInfo(ctx, sa.provider, sa.oauth2Config.TokenSource(ctx, &token))
if err != nil {
var reqErr *requestError
if !errors.As(err, &reqErr) {
return nil, false, errors.Wrap(err, "UserInfo request failed unexpectedly")
}
if reqErr.Response.StatusCode != http.StatusUnauthorized {
return nil, false, errors.Wrapf(err, "UserInfo request with unexpected code '%d'", reqErr.Response.StatusCode)
}
// Access token has expired
logger.Info("UserInfo token has expired")
// XXX: With the current abstraction, an authenticator doesn't have
// access to the ResponseWriter and thus can't set a cookie. This
// means that the cookie will remain at the user's browser but it
// will be replaced after the user logs in again.
err = revokeOIDCSession(ctx, httptest.NewRecorder(), session,
sa.provider, sa.oauth2Config, sa.caBundle)
if err != nil {
logger.Errorf("Failed to revoke tokens: %v", err)
}
return nil, false, nil
}
}
// Data written at a previous version might not have groups stored, so
// default to an empty list of strings.
// TODO: Consolidate all session serialization/deserialization in one place.
groups, ok := session.Values[userSessionGroups].([]string)
if !ok {
groups = []string{}
}
extra := map[string][]string{"auth-method": {authMethod}}
resp := &authenticator.Response{
User: &user.DefaultInfo{
Name: session.Values[userSessionUserID].(string),
Groups: groups,
Extra: extra,
},
}
return resp, true, nil
}