Add new federated:id scope that causes Dex to add a federated_claims claim containing the connector_id and user_id to the ID token

This commit is contained in:
Vy-Shane Xie 2018-01-23 21:15:20 +08:00
parent 1dbecefadf
commit b03c85e56e
2 changed files with 25 additions and 1 deletions

View File

@ -12,6 +12,7 @@ The following is the exhaustive list of scopes supported by dex:
| `email` | ID token claims should include the end user's email and if that email was verified by an upstream provider. | | `email` | ID token claims should include the end user's email and if that email was verified by an upstream provider. |
| `profile` | ID token claims should include the username of the end user. | | `profile` | ID token claims should include the username of the end user. |
| `groups` | ID token claims should include a list of groups the end user is a member of. | | `groups` | ID token claims should include a list of groups the end user is a member of. |
| `federated:id` | ID token claims should include information from the ID provider. The token will contain the connector ID and the user ID assigned at the provider. |
| `offline_access` | Token response should include a refresh token. Doesn't work in combinations with some connectors, notability the [SAML connector][saml-connector] ignores this scope. | | `offline_access` | Token response should include a refresh token. Doesn't work in combinations with some connectors, notability the [SAML connector][saml-connector] ignores this scope. |
| `audience:server:client_id:( client-id )` | Dynamic scope indicating that the ID token should be issued on behalf of another client. See the _"Cross-client trust and authorized party"_ section below. | | `audience:server:client_id:( client-id )` | Dynamic scope indicating that the ID token should be issued on behalf of another client. See the _"Cross-client trust and authorized party"_ section below. |
@ -22,10 +23,20 @@ Beyond the [required OpenID Connect claims][core-claims], and a handful of [stan
| Name | Description | | Name | Description |
| ---- | ------------| | ---- | ------------|
| `groups` | A list of strings representing the groups a user is a member of. | | `groups` | A list of strings representing the groups a user is a member of. |
| `federated_claims` | The connector ID and the user ID assigned to the user at the provider. |
| `email` | The email of the user. | | `email` | The email of the user. |
| `email_verified` | If the upstream provider has verified the email. | | `email_verified` | If the upstream provider has verified the email. |
| `name` | User's display name. | | `name` | User's display name. |
The `federated_claims` claim has the following format:
```json
"federated_claims": {
"connector_id": "github",
"user_id": "110272483197731336751"
}
```
## Cross-client trust and authorized party ## Cross-client trust and authorized party
Dex has the ability to issue ID tokens to clients on behalf of other clients. In OpenID Connect terms, this means the ID token's `aud` (audience) claim being a different client ID than the client that performed the login. Dex has the ability to issue ID tokens to clients on behalf of other clients. In OpenID Connect terms, this means the ID token's `aud` (audience) claim being a different client ID than the client that performed the login.

View File

@ -107,6 +107,7 @@ const (
scopeGroups = "groups" scopeGroups = "groups"
scopeEmail = "email" scopeEmail = "email"
scopeProfile = "profile" scopeProfile = "profile"
scopeFederatedID = "federated:id"
scopeCrossClientPrefix = "audience:server:client_id:" scopeCrossClientPrefix = "audience:server:client_id:"
) )
@ -255,6 +256,13 @@ type idTokenClaims struct {
Groups []string `json:"groups,omitempty"` Groups []string `json:"groups,omitempty"`
Name string `json:"name,omitempty"` Name string `json:"name,omitempty"`
FederatedIDClaims *federatedIDClaims `json:"federated_claims,omitempty"`
}
type federatedIDClaims struct {
ConnectorID string `json:"connector_id,omitempty"`
UserID string `json:"user_id,omitempty"`
} }
func (s *Server) newIDToken(clientID string, claims storage.Claims, scopes []string, nonce, accessToken, connID string) (idToken string, expiry time.Time, err error) { func (s *Server) newIDToken(clientID string, claims storage.Claims, scopes []string, nonce, accessToken, connID string) (idToken string, expiry time.Time, err error) {
@ -313,6 +321,11 @@ func (s *Server) newIDToken(clientID string, claims storage.Claims, scopes []str
tok.Groups = claims.Groups tok.Groups = claims.Groups
case scope == scopeProfile: case scope == scopeProfile:
tok.Name = claims.Username tok.Name = claims.Username
case scope == scopeFederatedID:
tok.FederatedIDClaims = &federatedIDClaims{
ConnectorID: connID,
UserID: claims.UserID,
}
default: default:
peerID, ok := parseCrossClientScope(scope) peerID, ok := parseCrossClientScope(scope)
if !ok { if !ok {
@ -405,7 +418,7 @@ func (s *Server) parseAuthorizationRequest(r *http.Request) (req storage.AuthReq
switch scope { switch scope {
case scopeOpenID: case scopeOpenID:
hasOpenIDScope = true hasOpenIDScope = true
case scopeOfflineAccess, scopeEmail, scopeProfile, scopeGroups: case scopeOfflineAccess, scopeEmail, scopeProfile, scopeGroups, scopeFederatedID:
default: default:
peerID, ok := parseCrossClientScope(scope) peerID, ok := parseCrossClientScope(scope)
if !ok { if !ok {