@@ -21,13 +21,14 @@ import (
2121 "encoding/json"
2222 "errors"
2323 "fmt"
24- "golang.org/x/oauth2"
2524 "html/template"
2625 "io/ioutil"
2726 "net/http"
2827 "strings"
2928 "time"
3029
30+ "golang.org/x/oauth2"
31+
3132 "github.com/coreos/go-oidc/v3/oidc"
3233
3334 "github.com/cesanta/glog"
@@ -52,6 +53,14 @@ type OIDCAuthConfig struct {
5253 HTTPTimeout int `yaml:"http_timeout,omitempty"`
5354 // the URL of the docker registry. Used to generate a full docker login command after authentication
5455 RegistryURL string `yaml:"registry_url,omitempty"`
56+ // --- optional ---
57+ // String claim to use for the username
58+ UserClaim string `yaml:"user_claim,omitempty"`
59+ // --- optional ---
60+ // []string to add as labels.
61+ LabelsClaims []string `yaml:"labels_claims,omitempty"`
62+ // --- optional ---
63+ Scopes []string `yaml:"scopes,omitempty"`
5564}
5665
5766// OIDCRefreshTokenResponse is sent by OIDC provider in response to the grant_type=refresh_token request.
@@ -66,14 +75,6 @@ type OIDCRefreshTokenResponse struct {
6675 ErrorDescription string `json:"error_description,omitempty"`
6776}
6877
69- // ProfileResponse is sent by the /userinfo endpoint or contained in the ID token.
70- // We use it to validate access token and (re)verify the email address associated with it.
71- type OIDCProfileResponse struct {
72- Email string `json:"email,omitempty"`
73- VerifiedEmail bool `json:"verified_email,omitempty"`
74- // There are more fields, but we only need email.
75- }
76-
7778// The specific OIDC authenticator
7879type OIDCAuth struct {
7980 config * OIDCAuthConfig
@@ -109,7 +110,7 @@ func NewOIDCAuth(c *OIDCAuthConfig) (*OIDCAuth, error) {
109110 ClientSecret : c .ClientSecret ,
110111 Endpoint : prov .Endpoint (),
111112 RedirectURL : c .RedirectURL ,
112- Scopes : [] string { oidc . ScopeOpenID , "email" } ,
113+ Scopes : c . Scopes ,
113114 }
114115 return & OIDCAuth {
115116 config : c ,
@@ -144,11 +145,12 @@ Executes tmpl for the OIDC login page.
144145*/
145146func (ga * OIDCAuth ) doOIDCAuthPage (rw http.ResponseWriter ) {
146147 if err := ga .tmpl .Execute (rw , struct {
147- AuthEndpoint , RedirectURI , ClientId string
148+ AuthEndpoint , RedirectURI , ClientId , Scope string
148149 }{
149150 AuthEndpoint : ga .provider .Endpoint ().AuthURL ,
150151 RedirectURI : ga .oauth .RedirectURL ,
151152 ClientId : ga .oauth .ClientID ,
153+ Scope : strings .Join (ga .config .Scopes , " " ),
152154 }); err != nil {
153155 http .Error (rw , fmt .Sprintf ("Template error: %s" , err ), http .StatusInternalServerError )
154156 }
@@ -191,32 +193,47 @@ func (ga *OIDCAuth) doOIDCAuthCreateToken(rw http.ResponseWriter, code string) {
191193 http .Error (rw , fmt .Sprintf ("Failed to verify ID token: %s" , err ), http .StatusInternalServerError )
192194 return
193195 }
194- var prof OIDCProfileResponse
195- if err := idTok .Claims (& prof ); err != nil {
196- http .Error (rw , fmt .Sprintf ("Failed to get mail information from ID token: %s" , err ), http .StatusInternalServerError )
196+ var claims map [ string ] interface {}
197+ if err := idTok .Claims (& claims ); err != nil {
198+ http .Error (rw , fmt .Sprintf ("Failed to get claims from ID token: %s" , err ), http .StatusInternalServerError )
197199 return
198200 }
199- if prof .Email == "" {
200- http .Error (rw , fmt .Sprintf ("No mail information given in ID token" ), http .StatusInternalServerError )
201+ username , _ := claims [ga .config .UserClaim ].(string )
202+ if username == "" {
203+ http .Error (rw , fmt .Sprintf ("No %q claim in ID token" , ga .config .UserClaim ), http .StatusInternalServerError )
201204 return
202205 }
203206
204- glog .V (2 ).Infof ("New OIDC auth token for %s (Current time: %s, expiration time: %s)" , prof . Email , time .Now ().String (), tok .Expiry .String ())
207+ glog .V (2 ).Infof ("New OIDC auth token for %s (Current time: %s, expiration time: %s)" , username , time .Now ().String (), tok .Expiry .String ())
205208
206209 dbVal := & TokenDBValue {
207210 TokenType : tok .TokenType ,
208211 AccessToken : tok .AccessToken ,
209212 RefreshToken : tok .RefreshToken ,
210213 ValidUntil : tok .Expiry .Add (time .Duration (- 30 ) * time .Second ),
214+ Labels : ga .getLabels (claims ),
211215 }
212- dp , err := ga .db .StoreToken (prof . Email , dbVal , true )
216+ dp , err := ga .db .StoreToken (username , dbVal , true )
213217 if err != nil {
214218 glog .Errorf ("Failed to record server token: %s" , err )
215219 http .Error (rw , "Failed to record server token: %s" , http .StatusInternalServerError )
216220 return
217221 }
218222
219- ga .doOIDCAuthResultPage (rw , prof .Email , dp )
223+ ga .doOIDCAuthResultPage (rw , username , dp )
224+ }
225+
226+ func (ga * OIDCAuth ) getLabels (claims map [string ]interface {}) api.Labels {
227+ labels := make (api.Labels , len (ga .config .LabelsClaims ))
228+ for _ , claim := range ga .config .LabelsClaims {
229+ values , _ := claims [claim ].([]interface {})
230+ for _ , v := range values {
231+ if str , _ := v .(string ); str != "" {
232+ labels [claim ] = append (labels [claim ], str )
233+ }
234+ }
235+ }
236+ return labels
220237}
221238
222239/*
@@ -294,8 +311,15 @@ func (ga *OIDCAuth) validateServerToken(user string) (*TokenDBValue, error) {
294311 glog .Warningf ("Token for %q failed validation: %s" , user , err )
295312 return nil , fmt .Errorf ("server token invalid: %s" , err )
296313 }
297- if tokUser .Email != user {
298- glog .Errorf ("token for wrong user: expected %s, found %s" , user , tokUser .Email )
314+
315+ var claims map [string ]interface {}
316+ if err := tokUser .Claims (& claims ); err != nil {
317+ glog .Errorf ("error retrieving claims: %v" , err )
318+ return nil , fmt .Errorf ("error retrieving claims: %w" , err )
319+ }
320+ claimUsername , _ := claims [ga .config .UserClaim ].(string )
321+ if claimUsername != user {
322+ glog .Errorf ("token for wrong user: expected %s, found %s" , user , claimUsername )
299323 return nil , fmt .Errorf ("found token for wrong user" )
300324 }
301325 texp := v .ValidUntil .Sub (time .Now ())
@@ -335,7 +359,15 @@ func (ga *OIDCAuth) Authenticate(user string, password api.PasswordString) (bool
335359 } else if err != nil {
336360 return false , nil , err
337361 }
338- return true , nil , nil
362+
363+ v , err := ga .db .GetValue (user )
364+ if err != nil || v == nil {
365+ if err == nil {
366+ err = errors .New ("no db value, please sign out and sign in again" )
367+ }
368+ return false , nil , err
369+ }
370+ return true , v .Labels , err
339371}
340372
341373func (ga * OIDCAuth ) Stop () {
0 commit comments