An Overview of OpenID Connect

Intro to OpenID Connect (basics)

This document attempts to provide a general overview of the OpenID Connect protocol, a flavor of OAuth2 that dex implements. While this document isn’t complete, we hope it provides enough information to get users up and running.

For an overview of custom claims, scopes, and client features implemented by dex, see this document.

OAuth2

OAuth2 should be familiar to anyone who’s used something similar to a “Login with Google” button. In these cases an application has chosen to let an outside provider, in this case Google, attest to your identity instead of having you set a username and password with the app itself.

The general flow for server side apps is:

  1. A new user visits an application.
  2. The application redirects the user to Google.
  3. The user logs into Google, then is asked if it’s okay to let the application view the user’s profile, post on their behalf, etc.
  4. If the user clicks okay, Google redirects the user back to the application with a code.
  5. The application redeems that code with provider for a token that can be used to access the authorized actions, such as viewing a users profile or posting on their wall.

In these cases, dex is acting as Google (called the “provider” in OpenID Connect) while clients apps redirect to it for the end user’s identity.

ID Tokens

Unfortunately the access token applications get from OAuth2 providers is completely opaque to the client and unique to the provider. The token you receive from Google will be completely different from the one you’d get from Twitter or GitHub.

OpenID Connect’s primary extension of OAuth2 is an additional token returned in the token response called the ID Token. This token is a JSON Web Token signed by the OpenID Connect server, with well known fields for user ID, name, email, etc. A typical token response from an OpenID Connect looks like (with less whitespace):

HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-store
Pragma: no-cache

{
 "access_token": "SlAV32hkKG",
 "token_type": "Bearer",
 "refresh_token": "8xLOxBtZp8",
 "expires_in": 3600,
 "id_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6IjFlOWdkazcifQ.ewogImlzcyI6ICJodHRwOi8vc2VydmVyLmV4YW1wbGUuY29tIiwKICJzdWIiOiAiMjQ4Mjg5NzYxMDAxIiwKICJhdWQiOiAiczZCaGRSa3F0MyIsCiAibm9uY2UiOiAibi0wUzZfV3pBMk1qIiwKICJleHAiOiAxMzExMjgxOTcwLAogImlhdCI6IDEzMTEyODA5NzAKfQ.ggW8hZ1EuVLuxNuuIJKX_V8a_OMXzR0EHR9R6jgdqrOOF4daGU96Sr_P6qJp6IcmD3HP99Obi1PRs-cwh3LO-p146waJ8IhehcwL7F09JdijmBqkvPeB2T9CJNqeGpe-gccMg4vfKjkM8FcGvnzZUN4_KSP0aAp1tOJ1zZwgjxqGByKHiOtX7TpdQyHE5lcMiKPXfEIQILVq0pc_E2DzL7emopWoaoZTF_m0_N0YzFC6g6EJbOEoRoSK5hoDalrcvRYLSrQAZZKflyuVCyixEoV9GfNQC3_osjzw2PAithfubEEBLuVVk4XUVrWOLrLl0nx7RkKU8NXNHq-rvKMzqg"
}

That ID Token is a JWT with three base64’d fields separated by dots. The first is a header, the second is a payload, and the third is a signature of the first two fields. When parsed we can see the payload of this value is.

{
  "iss": "http://server.example.com",
  "sub": "248289761001",
  "aud": "s6BhdRkqt3",
  "nonce": "n-0S6_WzA2Mj",
  "exp": 1311281970,
  "iat": 1311280970
}

This has a few interesting fields such as

  • The server that issued this token (iss).
  • The token’s subject (sub). In this case a unique ID of the end user.
  • The token’s audience (aud). The ID of the OAuth2 client this was issued for.

A real world token would have additional claims like the user’s name, email, groups, etc.

{
  "iss": "https://dex.example.com/",
  "sub": "R29vZCBqb2IhIEdpdmUgdXMgYSBzdGFyIG9uIGdpdGh1Yg",
  "aud": [
    "kubernetes",
    "kubeconfig-generator"
  ],
  "exp": 1712945837,
  "iat": 1712945237,
  "azp": "kubeconfig-generator",
  "at_hash": "OamCo8c60Zdj3dVho3Km5oxA",
  "c_hash": "HT04XtwtlUhfHvm7zf19qsGw",
  "email": "maksim.nabokikh@palark.com",
  "email_verified": true,
  "groups": [
    "administrators",
    "developers"
  ],
  "name": "Maksim Nabokikh",
  "preferred_username": "maksim.nabokikh"
}

Discovery

OpenID Connect servers have a discovery mechanism for OAuth2 endpoints, scopes supported, and indications of various other OpenID Connect features.

$ curl http://127.0.0.1:5556/dex/.well-known/openid-configuration
{
  "issuer": "http://127.0.0.1:5556",
  "authorization_endpoint": "http://127.0.0.1:5556/auth",
  "token_endpoint": "http://127.0.0.1:5556/token",
  "jwks_uri": "http://127.0.0.1:5556/keys",
  "response_types_supported": [
    "code"
  ],
  "subject_types_supported": [
    "public"
  ],
  "id_token_signing_alg_values_supported": [
    "RS256"
  ],
  "scopes_supported": [
    "openid",
    "email",
    "profile"
  ]
}

Importantly, we’ve discovered the authorization endpoint, token endpoint, and the location of the server’s public keys. OAuth2 clients should be able to use the token and auth endpoints immediately, while a JOSE library can be used to parse the keys. The keys endpoint returns a JSON Web Key Set of public keys that will look something like this:

$ curl http://127.0.0.1:5556/dex/keys
{
  "keys": [
    {
      "use": "sig",
      "kty": "RSA",
      "kid": "5d19a0fde5547960f4edaa1e1e8293e5534169ba",
      "alg": "RS256",
      "n": "5TAXCxkAQqHEqO0InP81z5F59PUzCe5ZNaDsD1SXzFe54BtXKn_V2a3K-BUNVliqMKhC2LByWLuI-A5ZlA5kXkbRFT05G0rusiM0rbkN2uvRmRCia4QlywE02xJKzeZV3KH6PldYqV_Jd06q1NV3WNqtcHN6MhnwRBfvkEIm7qWdPZ_mVK7vayfEnOCFRa7EZqr-U_X84T0-50wWkHTa0AfnyVvSMK1eKL-4yc26OWkmjh5ALfQFtnsz30Y2TOJdXtEfn35Y_882dNBDYBxtJV4PaSjXCxhiaIuBHp5uRS1INyMXCx2ve22ASNx_ERorv6BlXQoMDqaML2bSiN9N8Q",
      "e": "AQAB"
    }
  ]
}