mirror of
https://github.com/oauth2-proxy/oauth2-proxy.git
synced 2025-06-08 23:56:36 +02:00
Add preferred_username support (OIDC provider) (#420)
* Add support for preferred username. * Add missing TOC entries. * Add note about preferred_username support. * Adjust tests. * Check on not implemented error for GetPreferredUsername() call. Co-authored-by: Felix Fontein <felix@fontein.de> Co-authored-by: Joel Speed <Joel.speed@hotmail.co.uk>
This commit is contained in:
parent
0bca3564b5
commit
d934309b44
@ -18,12 +18,16 @@ Valid providers are :
|
|||||||
- [Keycloak](#keycloak-auth-provider)
|
- [Keycloak](#keycloak-auth-provider)
|
||||||
- [GitLab](#gitlab-auth-provider)
|
- [GitLab](#gitlab-auth-provider)
|
||||||
- [LinkedIn](#linkedin-auth-provider)
|
- [LinkedIn](#linkedin-auth-provider)
|
||||||
|
- [Microsoft Azure AD](#microsoft-azure-ad-provider)
|
||||||
|
- [OpenID Connect](#openid-connect-provider)
|
||||||
- [login.gov](#logingov-provider)
|
- [login.gov](#logingov-provider)
|
||||||
- [Nextcloud](#nextcloud-provider)
|
- [Nextcloud](#nextcloud-provider)
|
||||||
- [DigitalOcean](#digitalocean-auth-provider)
|
- [DigitalOcean](#digitalocean-auth-provider)
|
||||||
|
|
||||||
The provider can be selected using the `provider` configuration value.
|
The provider can be selected using the `provider` configuration value.
|
||||||
|
|
||||||
|
Please note that not all provides support all claims. The `preferred_username` claim is currently only supported by the OpenID Connect provider.
|
||||||
|
|
||||||
### Google Auth Provider
|
### Google Auth Provider
|
||||||
|
|
||||||
For Google, the registration steps are:
|
For Google, the registration steps are:
|
||||||
|
@ -73,10 +73,10 @@ An example [oauth2_proxy.cfg]({{ site.gitweb }}/contrib/oauth2_proxy.cfg.example
|
|||||||
| `-oidc-jwks-url` | string | OIDC JWKS URI for token verification; required if OIDC discovery is disabled | |
|
| `-oidc-jwks-url` | string | OIDC JWKS URI for token verification; required if OIDC discovery is disabled | |
|
||||||
| `-pass-access-token` | bool | pass OAuth access_token to upstream via X-Forwarded-Access-Token header | false |
|
| `-pass-access-token` | bool | pass OAuth access_token to upstream via X-Forwarded-Access-Token header | false |
|
||||||
| `-pass-authorization-header` | bool | pass OIDC IDToken to upstream via Authorization Bearer header | false |
|
| `-pass-authorization-header` | bool | pass OIDC IDToken to upstream via Authorization Bearer header | false |
|
||||||
| `-pass-basic-auth` | bool | pass HTTP Basic Auth, X-Forwarded-User and X-Forwarded-Email information to upstream | true |
|
| `-pass-basic-auth` | bool | pass HTTP Basic Auth, X-Forwarded-User, X-Forwarded-Email and X-Forwarded-Preferred-Username information to upstream | true |
|
||||||
| `-prefer-email-to-user` | bool | Prefer to use the Email address as the Username when passing information to upstream. Will only use Username if Email is unavailable, eg. htaccess authentication. | false |
|
| `-prefer-email-to-user` | bool | Prefer to use the Email address as the Username when passing information to upstream. Will only use Username if Email is unavailable, eg. htaccess authentication. | false |
|
||||||
| `-pass-host-header` | bool | pass the request Host Header to upstream | true |
|
| `-pass-host-header` | bool | pass the request Host Header to upstream | true |
|
||||||
| `-pass-user-headers` | bool | pass X-Forwarded-User and X-Forwarded-Email information to upstream | true |
|
| `-pass-user-headers` | bool | pass X-Forwarded-User, X-Forwarded-Email and X-Forwarded-Preferred-Username information to upstream | true |
|
||||||
| `-profile-url` | string | Profile access endpoint | |
|
| `-profile-url` | string | Profile access endpoint | |
|
||||||
| `-provider` | string | OAuth provider | google |
|
| `-provider` | string | OAuth provider | google |
|
||||||
| `-provider-display-name` | string | Override the provider's name with the given string; used for the sign-in page | (depends on provider) |
|
| `-provider-display-name` | string | Override the provider's name with the given string; used for the sign-in page | (depends on provider) |
|
||||||
@ -98,7 +98,7 @@ An example [oauth2_proxy.cfg]({{ site.gitweb }}/contrib/oauth2_proxy.cfg.example
|
|||||||
| `-reverse-proxy` | bool | are we running behind a reverse proxy, controls whether headers like X-Real-Ip are accepted | false |
|
| `-reverse-proxy` | bool | are we running behind a reverse proxy, controls whether headers like X-Real-Ip are accepted | false |
|
||||||
| `-scope` | string | OAuth scope specification | |
|
| `-scope` | string | OAuth scope specification | |
|
||||||
| `-session-store-type` | string | [Session data storage backend](configuration/sessions); redis or cookie | cookie |
|
| `-session-store-type` | string | [Session data storage backend](configuration/sessions); redis or cookie | cookie |
|
||||||
| `-set-xauthrequest` | bool | set X-Auth-Request-User and X-Auth-Request-Email response headers (useful in Nginx auth_request mode) | false |
|
| `-set-xauthrequest` | bool | set X-Auth-Request-User, X-Auth-Request-Email and X-Auth-Request-Preferred-Username response headers (useful in Nginx auth_request mode) | false |
|
||||||
| `-set-authorization-header` | bool | set Authorization Bearer response header (useful in Nginx auth_request mode) | false |
|
| `-set-authorization-header` | bool | set Authorization Bearer response header (useful in Nginx auth_request mode) | false |
|
||||||
| `-signature-key` | string | GAP-Signature request signature key (algorithm:secretkey) | |
|
| `-signature-key` | string | GAP-Signature request signature key (algorithm:secretkey) | |
|
||||||
| `-silence-ping-logging` | bool | disable logging of requests to ping endpoint | false |
|
| `-silence-ping-logging` | bool | disable logging of requests to ping endpoint | false |
|
||||||
|
@ -48,6 +48,7 @@ var SignatureHeaders = []string{
|
|||||||
"Authorization",
|
"Authorization",
|
||||||
"X-Forwarded-User",
|
"X-Forwarded-User",
|
||||||
"X-Forwarded-Email",
|
"X-Forwarded-Email",
|
||||||
|
"X-Forwarded-Preferred-User",
|
||||||
"X-Forwarded-Access-Token",
|
"X-Forwarded-Access-Token",
|
||||||
"Cookie",
|
"Cookie",
|
||||||
"Gap-Auth",
|
"Gap-Auth",
|
||||||
@ -352,6 +353,13 @@ func (p *OAuthProxy) redeemCode(host, code string) (s *sessionsapi.SessionState,
|
|||||||
s.Email, err = p.provider.GetEmailAddress(s)
|
s.Email, err = p.provider.GetEmailAddress(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if s.PreferredUsername == "" {
|
||||||
|
s.PreferredUsername, err = p.provider.GetPreferredUsername(s)
|
||||||
|
if err != nil && err.Error() == "not implemented" {
|
||||||
|
err = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if s.User == "" {
|
if s.User == "" {
|
||||||
s.User, err = p.provider.GetUserName(s)
|
s.User, err = p.provider.GetUserName(s)
|
||||||
if err != nil && err.Error() == "not implemented" {
|
if err != nil && err.Error() == "not implemented" {
|
||||||
@ -670,7 +678,7 @@ func (p *OAuthProxy) SignIn(rw http.ResponseWriter, req *http.Request) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//UserInfo endpoint outputs session email in JSON format
|
//UserInfo endpoint outputs session email and preferred username in JSON format
|
||||||
func (p *OAuthProxy) UserInfo(rw http.ResponseWriter, req *http.Request) {
|
func (p *OAuthProxy) UserInfo(rw http.ResponseWriter, req *http.Request) {
|
||||||
|
|
||||||
session, err := p.getAuthenticatedSession(rw, req)
|
session, err := p.getAuthenticatedSession(rw, req)
|
||||||
@ -679,8 +687,12 @@ func (p *OAuthProxy) UserInfo(rw http.ResponseWriter, req *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
userInfo := struct {
|
userInfo := struct {
|
||||||
Email string `json:"email"`
|
Email string `json:"email"`
|
||||||
}{session.Email}
|
PreferredUsername string `json:"preferredUsername,omitempty"`
|
||||||
|
}{
|
||||||
|
Email: session.Email,
|
||||||
|
PreferredUsername: session.PreferredUsername,
|
||||||
|
}
|
||||||
rw.Header().Set("Content-Type", "application/json")
|
rw.Header().Set("Content-Type", "application/json")
|
||||||
rw.WriteHeader(http.StatusOK)
|
rw.WriteHeader(http.StatusOK)
|
||||||
json.NewEncoder(rw).Encode(userInfo)
|
json.NewEncoder(rw).Encode(userInfo)
|
||||||
@ -939,6 +951,11 @@ func (p *OAuthProxy) addHeadersForProxying(rw http.ResponseWriter, req *http.Req
|
|||||||
req.Header.Del("X-Forwarded-Email")
|
req.Header.Del("X-Forwarded-Email")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if session.PreferredUsername != "" {
|
||||||
|
req.Header["X-Forwarded-Preferred-Username"] = []string{session.PreferredUsername}
|
||||||
|
} else {
|
||||||
|
req.Header.Del("X-Forwarded-Preferred-Username")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if p.PassUserHeaders {
|
if p.PassUserHeaders {
|
||||||
@ -948,6 +965,11 @@ func (p *OAuthProxy) addHeadersForProxying(rw http.ResponseWriter, req *http.Req
|
|||||||
} else {
|
} else {
|
||||||
req.Header.Del("X-Forwarded-Email")
|
req.Header.Del("X-Forwarded-Email")
|
||||||
}
|
}
|
||||||
|
if session.PreferredUsername != "" {
|
||||||
|
req.Header["X-Forwarded-Preferred-Username"] = []string{session.PreferredUsername}
|
||||||
|
} else {
|
||||||
|
req.Header.Del("X-Forwarded-Preferred-Username")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if p.SetXAuthRequest {
|
if p.SetXAuthRequest {
|
||||||
@ -957,6 +979,11 @@ func (p *OAuthProxy) addHeadersForProxying(rw http.ResponseWriter, req *http.Req
|
|||||||
} else {
|
} else {
|
||||||
rw.Header().Del("X-Auth-Request-Email")
|
rw.Header().Del("X-Auth-Request-Email")
|
||||||
}
|
}
|
||||||
|
if session.PreferredUsername != "" {
|
||||||
|
rw.Header().Set("X-Auth-Request-Preferred-Username", session.PreferredUsername)
|
||||||
|
} else {
|
||||||
|
rw.Header().Del("X-Auth-Request-Preferred-Username")
|
||||||
|
}
|
||||||
|
|
||||||
if p.PassAccessToken {
|
if p.PassAccessToken {
|
||||||
if session.AccessToken != "" {
|
if session.AccessToken != "" {
|
||||||
@ -1066,9 +1093,10 @@ func (p *OAuthProxy) GetJwtSession(req *http.Request) (*sessionsapi.SessionState
|
|||||||
}
|
}
|
||||||
|
|
||||||
var claims struct {
|
var claims struct {
|
||||||
Subject string `json:"sub"`
|
Subject string `json:"sub"`
|
||||||
Email string `json:"email"`
|
Email string `json:"email"`
|
||||||
Verified *bool `json:"email_verified"`
|
Verified *bool `json:"email_verified"`
|
||||||
|
PreferredUsername string `json:"preferred_username"`
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := bearerToken.Claims(&claims); err != nil {
|
if err := bearerToken.Claims(&claims); err != nil {
|
||||||
@ -1084,12 +1112,13 @@ func (p *OAuthProxy) GetJwtSession(req *http.Request) (*sessionsapi.SessionState
|
|||||||
}
|
}
|
||||||
|
|
||||||
session = &sessionsapi.SessionState{
|
session = &sessionsapi.SessionState{
|
||||||
AccessToken: rawBearerToken,
|
AccessToken: rawBearerToken,
|
||||||
IDToken: rawBearerToken,
|
IDToken: rawBearerToken,
|
||||||
RefreshToken: "",
|
RefreshToken: "",
|
||||||
ExpiresOn: bearerToken.Expiry,
|
ExpiresOn: bearerToken.Expiry,
|
||||||
Email: claims.Email,
|
Email: claims.Email,
|
||||||
User: claims.Email,
|
User: claims.Email,
|
||||||
|
PreferredUsername: claims.PreferredUsername,
|
||||||
}
|
}
|
||||||
return session, nil
|
return session, nil
|
||||||
}
|
}
|
||||||
|
@ -12,13 +12,14 @@ import (
|
|||||||
|
|
||||||
// SessionState is used to store information about the currently authenticated user session
|
// SessionState is used to store information about the currently authenticated user session
|
||||||
type SessionState struct {
|
type SessionState struct {
|
||||||
AccessToken string `json:",omitempty"`
|
AccessToken string `json:",omitempty"`
|
||||||
IDToken string `json:",omitempty"`
|
IDToken string `json:",omitempty"`
|
||||||
CreatedAt time.Time `json:"-"`
|
CreatedAt time.Time `json:"-"`
|
||||||
ExpiresOn time.Time `json:"-"`
|
ExpiresOn time.Time `json:"-"`
|
||||||
RefreshToken string `json:",omitempty"`
|
RefreshToken string `json:",omitempty"`
|
||||||
Email string `json:",omitempty"`
|
Email string `json:",omitempty"`
|
||||||
User string `json:",omitempty"`
|
User string `json:",omitempty"`
|
||||||
|
PreferredUsername string `json:",omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// SessionStateJSON is used to encode SessionState into JSON without exposing time.Time zero value
|
// SessionStateJSON is used to encode SessionState into JSON without exposing time.Time zero value
|
||||||
@ -46,7 +47,7 @@ func (s *SessionState) Age() time.Duration {
|
|||||||
|
|
||||||
// String constructs a summary of the session state
|
// String constructs a summary of the session state
|
||||||
func (s *SessionState) String() string {
|
func (s *SessionState) String() string {
|
||||||
o := fmt.Sprintf("Session{email:%s user:%s", s.Email, s.User)
|
o := fmt.Sprintf("Session{email:%s user:%s PreferredUsername:%s", s.Email, s.User, s.PreferredUsername)
|
||||||
if s.AccessToken != "" {
|
if s.AccessToken != "" {
|
||||||
o += " token:true"
|
o += " token:true"
|
||||||
}
|
}
|
||||||
@ -72,6 +73,7 @@ func (s *SessionState) EncodeSessionState(c *encryption.Cipher) (string, error)
|
|||||||
// Store only Email and User when cipher is unavailable
|
// Store only Email and User when cipher is unavailable
|
||||||
ss.Email = s.Email
|
ss.Email = s.Email
|
||||||
ss.User = s.User
|
ss.User = s.User
|
||||||
|
ss.PreferredUsername = s.PreferredUsername
|
||||||
} else {
|
} else {
|
||||||
ss = *s
|
ss = *s
|
||||||
var err error
|
var err error
|
||||||
@ -87,6 +89,12 @@ func (s *SessionState) EncodeSessionState(c *encryption.Cipher) (string, error)
|
|||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if ss.PreferredUsername != "" {
|
||||||
|
ss.PreferredUsername, err = c.Encrypt(ss.PreferredUsername)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
}
|
||||||
if ss.AccessToken != "" {
|
if ss.AccessToken != "" {
|
||||||
ss.AccessToken, err = c.Encrypt(ss.AccessToken)
|
ss.AccessToken, err = c.Encrypt(ss.AccessToken)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -199,8 +207,9 @@ func DecodeSessionState(v string, c *encryption.Cipher) (*SessionState, error) {
|
|||||||
if c == nil {
|
if c == nil {
|
||||||
// Load only Email and User when cipher is unavailable
|
// Load only Email and User when cipher is unavailable
|
||||||
ss = &SessionState{
|
ss = &SessionState{
|
||||||
Email: ss.Email,
|
Email: ss.Email,
|
||||||
User: ss.User,
|
User: ss.User,
|
||||||
|
PreferredUsername: ss.PreferredUsername,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Backward compatibility with using unencrypted Email
|
// Backward compatibility with using unencrypted Email
|
||||||
@ -217,6 +226,12 @@ func DecodeSessionState(v string, c *encryption.Cipher) (*SessionState, error) {
|
|||||||
ss.User = decryptedUser
|
ss.User = decryptedUser
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if ss.PreferredUsername != "" {
|
||||||
|
ss.PreferredUsername, err = c.Decrypt(ss.PreferredUsername)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
if ss.AccessToken != "" {
|
if ss.AccessToken != "" {
|
||||||
ss.AccessToken, err = c.Decrypt(ss.AccessToken)
|
ss.AccessToken, err = c.Decrypt(ss.AccessToken)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -19,12 +19,13 @@ func TestSessionStateSerialization(t *testing.T) {
|
|||||||
c2, err := encryption.NewCipher([]byte(altSecret))
|
c2, err := encryption.NewCipher([]byte(altSecret))
|
||||||
assert.Equal(t, nil, err)
|
assert.Equal(t, nil, err)
|
||||||
s := &sessions.SessionState{
|
s := &sessions.SessionState{
|
||||||
Email: "user@domain.com",
|
Email: "user@domain.com",
|
||||||
AccessToken: "token1234",
|
PreferredUsername: "user",
|
||||||
IDToken: "rawtoken1234",
|
AccessToken: "token1234",
|
||||||
CreatedAt: time.Now(),
|
IDToken: "rawtoken1234",
|
||||||
ExpiresOn: time.Now().Add(time.Duration(1) * time.Hour),
|
CreatedAt: time.Now(),
|
||||||
RefreshToken: "refresh4321",
|
ExpiresOn: time.Now().Add(time.Duration(1) * time.Hour),
|
||||||
|
RefreshToken: "refresh4321",
|
||||||
}
|
}
|
||||||
encoded, err := s.EncodeSessionState(c)
|
encoded, err := s.EncodeSessionState(c)
|
||||||
assert.Equal(t, nil, err)
|
assert.Equal(t, nil, err)
|
||||||
@ -34,6 +35,7 @@ func TestSessionStateSerialization(t *testing.T) {
|
|||||||
assert.Equal(t, nil, err)
|
assert.Equal(t, nil, err)
|
||||||
assert.Equal(t, "user@domain.com", ss.User)
|
assert.Equal(t, "user@domain.com", ss.User)
|
||||||
assert.Equal(t, s.Email, ss.Email)
|
assert.Equal(t, s.Email, ss.Email)
|
||||||
|
assert.Equal(t, s.PreferredUsername, ss.PreferredUsername)
|
||||||
assert.Equal(t, s.AccessToken, ss.AccessToken)
|
assert.Equal(t, s.AccessToken, ss.AccessToken)
|
||||||
assert.Equal(t, s.IDToken, ss.IDToken)
|
assert.Equal(t, s.IDToken, ss.IDToken)
|
||||||
assert.Equal(t, s.CreatedAt.Unix(), ss.CreatedAt.Unix())
|
assert.Equal(t, s.CreatedAt.Unix(), ss.CreatedAt.Unix())
|
||||||
@ -46,6 +48,7 @@ func TestSessionStateSerialization(t *testing.T) {
|
|||||||
assert.Equal(t, nil, err)
|
assert.Equal(t, nil, err)
|
||||||
assert.NotEqual(t, "user@domain.com", ss.User)
|
assert.NotEqual(t, "user@domain.com", ss.User)
|
||||||
assert.NotEqual(t, s.Email, ss.Email)
|
assert.NotEqual(t, s.Email, ss.Email)
|
||||||
|
assert.NotEqual(t, s.PreferredUsername, ss.PreferredUsername)
|
||||||
assert.Equal(t, s.CreatedAt.Unix(), ss.CreatedAt.Unix())
|
assert.Equal(t, s.CreatedAt.Unix(), ss.CreatedAt.Unix())
|
||||||
assert.Equal(t, s.ExpiresOn.Unix(), ss.ExpiresOn.Unix())
|
assert.Equal(t, s.ExpiresOn.Unix(), ss.ExpiresOn.Unix())
|
||||||
assert.NotEqual(t, s.AccessToken, ss.AccessToken)
|
assert.NotEqual(t, s.AccessToken, ss.AccessToken)
|
||||||
@ -59,12 +62,13 @@ func TestSessionStateSerializationWithUser(t *testing.T) {
|
|||||||
c2, err := encryption.NewCipher([]byte(altSecret))
|
c2, err := encryption.NewCipher([]byte(altSecret))
|
||||||
assert.Equal(t, nil, err)
|
assert.Equal(t, nil, err)
|
||||||
s := &sessions.SessionState{
|
s := &sessions.SessionState{
|
||||||
User: "just-user",
|
User: "just-user",
|
||||||
Email: "user@domain.com",
|
PreferredUsername: "ju",
|
||||||
AccessToken: "token1234",
|
Email: "user@domain.com",
|
||||||
CreatedAt: time.Now(),
|
AccessToken: "token1234",
|
||||||
ExpiresOn: time.Now().Add(time.Duration(1) * time.Hour),
|
CreatedAt: time.Now(),
|
||||||
RefreshToken: "refresh4321",
|
ExpiresOn: time.Now().Add(time.Duration(1) * time.Hour),
|
||||||
|
RefreshToken: "refresh4321",
|
||||||
}
|
}
|
||||||
encoded, err := s.EncodeSessionState(c)
|
encoded, err := s.EncodeSessionState(c)
|
||||||
assert.Equal(t, nil, err)
|
assert.Equal(t, nil, err)
|
||||||
@ -74,6 +78,7 @@ func TestSessionStateSerializationWithUser(t *testing.T) {
|
|||||||
assert.Equal(t, nil, err)
|
assert.Equal(t, nil, err)
|
||||||
assert.Equal(t, s.User, ss.User)
|
assert.Equal(t, s.User, ss.User)
|
||||||
assert.Equal(t, s.Email, ss.Email)
|
assert.Equal(t, s.Email, ss.Email)
|
||||||
|
assert.Equal(t, s.PreferredUsername, ss.PreferredUsername)
|
||||||
assert.Equal(t, s.AccessToken, ss.AccessToken)
|
assert.Equal(t, s.AccessToken, ss.AccessToken)
|
||||||
assert.Equal(t, s.CreatedAt.Unix(), ss.CreatedAt.Unix())
|
assert.Equal(t, s.CreatedAt.Unix(), ss.CreatedAt.Unix())
|
||||||
assert.Equal(t, s.ExpiresOn.Unix(), ss.ExpiresOn.Unix())
|
assert.Equal(t, s.ExpiresOn.Unix(), ss.ExpiresOn.Unix())
|
||||||
@ -85,6 +90,7 @@ func TestSessionStateSerializationWithUser(t *testing.T) {
|
|||||||
assert.Equal(t, nil, err)
|
assert.Equal(t, nil, err)
|
||||||
assert.NotEqual(t, s.User, ss.User)
|
assert.NotEqual(t, s.User, ss.User)
|
||||||
assert.NotEqual(t, s.Email, ss.Email)
|
assert.NotEqual(t, s.Email, ss.Email)
|
||||||
|
assert.NotEqual(t, s.PreferredUsername, ss.PreferredUsername)
|
||||||
assert.Equal(t, s.CreatedAt.Unix(), ss.CreatedAt.Unix())
|
assert.Equal(t, s.CreatedAt.Unix(), ss.CreatedAt.Unix())
|
||||||
assert.Equal(t, s.ExpiresOn.Unix(), ss.ExpiresOn.Unix())
|
assert.Equal(t, s.ExpiresOn.Unix(), ss.ExpiresOn.Unix())
|
||||||
assert.NotEqual(t, s.AccessToken, ss.AccessToken)
|
assert.NotEqual(t, s.AccessToken, ss.AccessToken)
|
||||||
@ -93,11 +99,12 @@ func TestSessionStateSerializationWithUser(t *testing.T) {
|
|||||||
|
|
||||||
func TestSessionStateSerializationNoCipher(t *testing.T) {
|
func TestSessionStateSerializationNoCipher(t *testing.T) {
|
||||||
s := &sessions.SessionState{
|
s := &sessions.SessionState{
|
||||||
Email: "user@domain.com",
|
Email: "user@domain.com",
|
||||||
AccessToken: "token1234",
|
PreferredUsername: "user",
|
||||||
CreatedAt: time.Now(),
|
AccessToken: "token1234",
|
||||||
ExpiresOn: time.Now().Add(time.Duration(1) * time.Hour),
|
CreatedAt: time.Now(),
|
||||||
RefreshToken: "refresh4321",
|
ExpiresOn: time.Now().Add(time.Duration(1) * time.Hour),
|
||||||
|
RefreshToken: "refresh4321",
|
||||||
}
|
}
|
||||||
encoded, err := s.EncodeSessionState(nil)
|
encoded, err := s.EncodeSessionState(nil)
|
||||||
assert.Equal(t, nil, err)
|
assert.Equal(t, nil, err)
|
||||||
@ -107,18 +114,20 @@ func TestSessionStateSerializationNoCipher(t *testing.T) {
|
|||||||
assert.Equal(t, nil, err)
|
assert.Equal(t, nil, err)
|
||||||
assert.Equal(t, "user@domain.com", ss.User)
|
assert.Equal(t, "user@domain.com", ss.User)
|
||||||
assert.Equal(t, s.Email, ss.Email)
|
assert.Equal(t, s.Email, ss.Email)
|
||||||
|
assert.Equal(t, s.PreferredUsername, ss.PreferredUsername)
|
||||||
assert.Equal(t, "", ss.AccessToken)
|
assert.Equal(t, "", ss.AccessToken)
|
||||||
assert.Equal(t, "", ss.RefreshToken)
|
assert.Equal(t, "", ss.RefreshToken)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSessionStateSerializationNoCipherWithUser(t *testing.T) {
|
func TestSessionStateSerializationNoCipherWithUser(t *testing.T) {
|
||||||
s := &sessions.SessionState{
|
s := &sessions.SessionState{
|
||||||
User: "just-user",
|
User: "just-user",
|
||||||
Email: "user@domain.com",
|
Email: "user@domain.com",
|
||||||
AccessToken: "token1234",
|
PreferredUsername: "user",
|
||||||
CreatedAt: time.Now(),
|
AccessToken: "token1234",
|
||||||
ExpiresOn: time.Now().Add(time.Duration(1) * time.Hour),
|
CreatedAt: time.Now(),
|
||||||
RefreshToken: "refresh4321",
|
ExpiresOn: time.Now().Add(time.Duration(1) * time.Hour),
|
||||||
|
RefreshToken: "refresh4321",
|
||||||
}
|
}
|
||||||
encoded, err := s.EncodeSessionState(nil)
|
encoded, err := s.EncodeSessionState(nil)
|
||||||
assert.Equal(t, nil, err)
|
assert.Equal(t, nil, err)
|
||||||
@ -128,6 +137,7 @@ func TestSessionStateSerializationNoCipherWithUser(t *testing.T) {
|
|||||||
assert.Equal(t, nil, err)
|
assert.Equal(t, nil, err)
|
||||||
assert.Equal(t, s.User, ss.User)
|
assert.Equal(t, s.User, ss.User)
|
||||||
assert.Equal(t, s.Email, ss.Email)
|
assert.Equal(t, s.Email, ss.Email)
|
||||||
|
assert.Equal(t, s.PreferredUsername, ss.PreferredUsername)
|
||||||
assert.Equal(t, "", ss.AccessToken)
|
assert.Equal(t, "", ss.AccessToken)
|
||||||
assert.Equal(t, "", ss.RefreshToken)
|
assert.Equal(t, "", ss.RefreshToken)
|
||||||
}
|
}
|
||||||
|
@ -121,6 +121,7 @@ func (p *OIDCProvider) redeemRefreshToken(s *sessions.SessionState) (err error)
|
|||||||
s.IDToken = newSession.IDToken
|
s.IDToken = newSession.IDToken
|
||||||
s.Email = newSession.Email
|
s.Email = newSession.Email
|
||||||
s.User = newSession.User
|
s.User = newSession.User
|
||||||
|
s.PreferredUsername = newSession.PreferredUsername
|
||||||
}
|
}
|
||||||
|
|
||||||
s.AccessToken = newSession.AccessToken
|
s.AccessToken = newSession.AccessToken
|
||||||
@ -165,6 +166,7 @@ func (p *OIDCProvider) createSessionState(token *oauth2.Token, idToken *oidc.IDT
|
|||||||
newSession.IDToken = token.Extra("id_token").(string)
|
newSession.IDToken = token.Extra("id_token").(string)
|
||||||
newSession.Email = claims.Email
|
newSession.Email = claims.Email
|
||||||
newSession.User = claims.Subject
|
newSession.User = claims.Subject
|
||||||
|
newSession.PreferredUsername = claims.PreferredUsername
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -233,7 +235,8 @@ func findClaimsFromIDToken(idToken *oidc.IDToken, accessToken string, profileURL
|
|||||||
}
|
}
|
||||||
|
|
||||||
type OIDCClaims struct {
|
type OIDCClaims struct {
|
||||||
Subject string `json:"sub"`
|
Subject string `json:"sub"`
|
||||||
Email string `json:"email"`
|
Email string `json:"email"`
|
||||||
Verified *bool `json:"email_verified"`
|
Verified *bool `json:"email_verified"`
|
||||||
|
PreferredUsername string `json:"preferred_username"`
|
||||||
}
|
}
|
||||||
|
@ -119,6 +119,11 @@ func (p *ProviderData) GetUserName(s *sessions.SessionState) (string, error) {
|
|||||||
return "", errors.New("not implemented")
|
return "", errors.New("not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetPreferredUsername returns the Account preferred username
|
||||||
|
func (p *ProviderData) GetPreferredUsername(s *sessions.SessionState) (string, error) {
|
||||||
|
return "", errors.New("not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
// ValidateGroup validates that the provided email exists in the configured provider
|
// ValidateGroup validates that the provided email exists in the configured provider
|
||||||
// email group(s).
|
// email group(s).
|
||||||
func (p *ProviderData) ValidateGroup(email string) bool {
|
func (p *ProviderData) ValidateGroup(email string) bool {
|
||||||
|
@ -10,6 +10,7 @@ type Provider interface {
|
|||||||
Data() *ProviderData
|
Data() *ProviderData
|
||||||
GetEmailAddress(*sessions.SessionState) (string, error)
|
GetEmailAddress(*sessions.SessionState) (string, error)
|
||||||
GetUserName(*sessions.SessionState) (string, error)
|
GetUserName(*sessions.SessionState) (string, error)
|
||||||
|
GetPreferredUsername(*sessions.SessionState) (string, error)
|
||||||
Redeem(string, string) (*sessions.SessionState, error)
|
Redeem(string, string) (*sessions.SessionState, error)
|
||||||
ValidateGroup(string) bool
|
ValidateGroup(string) bool
|
||||||
ValidateSessionState(*sessions.SessionState) bool
|
ValidateSessionState(*sessions.SessionState) bool
|
||||||
|
Loading…
x
Reference in New Issue
Block a user