This is the multi-page printable view of this section. Click here to print.

Return to the regular view of this page.

Configuration

Configuring general settings for Dex

1 - Tokens

Types of tokens and expiration settings

Overview

ID Tokens are an OAuth2 extension introduced by OpenID Connect and Dex’s primary feature. ID Tokens are JSON Web Tokens (JWTs) signed by Dex and returned as part of the OAuth2 response that attest to the end user’s identity. An example JWT might look like:

eyJhbGciOiJSUzI1NiIsImtpZCI6IjlkNDQ3NDFmNzczYjkzOGNmNjVkZDMyNjY4NWI4NjE4MGMzMjRkOTkifQ.eyJpc3MiOiJodHRwOi8vMTI3LjAuMC4xOjU1NTYvZGV4Iiwic3ViIjoiQ2djeU16UXlOelE1RWdabmFYUm9kV0kiLCJhdWQiOiJleGFtcGxlLWFwcCIsImV4cCI6MTQ5Mjg4MjA0MiwiaWF0IjoxNDkyNzk1NjQyLCJhdF9oYXNoIjoiYmk5NmdPWFpTaHZsV1l0YWw5RXFpdyIsImVtYWlsIjoiZXJpYy5jaGlhbmdAY29yZW9zLmNvbSIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJncm91cHMiOlsiYWRtaW5zIiwiZGV2ZWxvcGVycyJdLCJuYW1lIjoiRXJpYyBDaGlhbmcifQ.OhROPq_0eP-zsQRjg87KZ4wGkjiQGnTi5QuG877AdJDb3R2ZCOk2Vkf5SdP8cPyb3VMqL32G4hLDayniiv8f1_ZXAde0sKrayfQ10XAXFgZl_P1yilkLdknxn6nbhDRVllpWcB12ki9vmAxklAr0B1C4kr5nI3-BZLrFcUR5sQbxwJj4oW1OuG6jJCNGHXGNTBTNEaM28eD-9nhfBeuBTzzO7BKwPsojjj4C9ogU4JQhGvm_l4yfVi0boSx8c0FX3JsiB0yLa1ZdJVWVl9m90XmbWRSD85pNDQHcWZP9hR6CMgbvGkZsgjG32qeRwUL_eNkNowSBNWLrGNPoON1gMg

ID Tokens contains standard claims assert which client app logged the user in, when the token expires, and the identity of the user.

{
  "iss": "http://127.0.0.1:5556/dex",
  "sub": "CgcyMzQyNzQ5EgZnaXRodWI",
  "aud": "example-app",
  "exp": 1492882042,
  "iat": 1492795642,
  "at_hash": "bi96gOXZShvlWYtal9Eqiw",
  "email": "jane.doe@coreos.com",
  "email_verified": true,
  "groups": [
    "admins",
    "developers"
  ],
  "name": "Jane Doe"
}

Because these tokens are signed by Dex and contain standard-based claims other services can consume them as service-to-service credentials. Systems that can already consume OpenID Connect ID Tokens issued by Dex include:

For details on how to request or validate an ID Token, see Writing apps that use Dex.”

Refresh tokens

Refresh tokens are credentials used to obtain access tokens. Refresh tokens are issued to the client by the authorization server and are used to obtain a new id token when the current id token becomes invalid or expires. Issuing a refresh token is optional and is provided by passing offline_access scope to Dex server.

NOTE: Some connectors do not support offline_access scope. You can find out which connectors support refresh tokens by looking into the connectors list.

Example of a server response with refresh token:

{
 "access_token": "eyJhbGciOiJSUzI1N...",
 "token_type": "Bearer",
 "refresh_token": "lxzzsvasxho5exvwkfa5zhefl",
 "expires_in": 3600,
 "id_token": "eyJhbGciO..."
}

NOTE: For every refresh of an id token, Dex issues a new refresh token. This security measure is called refresh token rotation and prevents someone stealing it. The idea is described in detail in the corresponding RFC.

Expiration and rotation settings

Dex has a section in the config file where you can specify expiration and rotation settings for id tokens and refresh tokens. NOTE: All duration options should be set in the format: number + time unit (s, m, h), e.g., 10m.

  • expiry - section for various expiration settings, including token settings:
    • idTokens - the lifetime of id_token. It is preferable to use short-lived id tokens.
    • deviceRequests - the lifetime of device_requests.
    • signingKeys - the lifetime of signing_token.
    • refreshTokens - section for various refresh token settings:
      • validIfNotUsedFor - invalidate a refresh token if it is not used for a specified amount of time.
      • absoluteLifetime - a stricter variant of the previous option, absolute lifetime of a refresh token. It forces users to reauthenticate and obtain a new refresh token.
      • disableRotation - completely disables every-request rotation. The user will also have to specify one of the previous refresh token options to keep refresh tokens secure when toggling this.
      • reuseInterval - allows getting the same refresh token from refresh endpoint within a specified interval, but only if the user’s request contains the previous refresh token.

NOTE: disableRotation and reuseInterval options help effectively deal with network lags, concurrent requests, and so on in tradeoff for security. Use them with caution.

2 - Scopes and Claims

Custom Scopes, Claims and Client Features

This document describes the set of OAuth2 and OpenID Connect features implemented by dex.

Scopes

The following is the exhaustive list of scopes supported by dex:

NameDescription
openidRequired scope for all login requests.
emailID token claims should include the end user’s email and if that email was verified by an upstream provider.
profileID token claims should include the username of the end user.
groupsID token claims should include a list of groups the end user is a member of.
federated:idID 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_accessToken response should include a refresh token. Doesn’t work in combinations with some connectors, notability the 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.

Custom claims

Beyond the required OpenID Connect claims, and a handful of standard claims, dex implements the following non-standard claims.

NameDescription
groupsA list of strings representing the groups a user is a member of.
federated_claimsThe connector ID and the user ID assigned to the user at the provider.
emailThe email of the user.
email_verifiedIf the upstream provider has verified the email.
nameUser’s display name.

The federated_claims claim has the following format:

"federated_claims": {
  "connector_id": "github",
  "user_id": "110272483197731336751"
}

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.

For example, this feature could be used to allow a web app to generate an ID token on behalf of a command line tool:

staticClients:
- id: web-app
  redirectURIs:
  - 'https://web-app.example.com/callback'
  name: 'Web app'
  secret: web-app-secret
  # It is also possible to fetch the secret from an injected environment variable
  # secretEnv: YOUR_INJECTED_SECRET

- id: cli-app
  redirectURIs:
  - 'https://cli-app.example.com/callback'
  name: 'Command line tool'
  secret: cli-app-secret
  # The command line tool lets the web app issue ID tokens on its behalf.
  trustedPeers:
  - web-app

Note that the command line tool must explicitly trust the web app using the trustedPeers field. The web app can then use the following scope to request an ID token that’s issued for the command line tool.

audience:server:client_id:cli-app

The ID token claims will then include the following audience and authorized party:

{
    "aud": "cli-app",
    "azp": "web-app",
    "email": "foo@bar.com",
    // other claims...
}

Public clients

Public clients are inspired by Google’s “Installed Applications” and are meant to impose restrictions on applications that don’t intend to keep their client secret private. Clients can be declared as public using the public config option.

staticClients:
- id: cli-app
  public: true
  name: 'CLI app'
  redirectURIs:
  - ...

If no redirectURIs are specified, public clients only support redirects that begin with “http://localhost” or a special “out-of-browser” URL “urn:ietf:wg:oauth:2.0:oob”. The latter triggers dex to display the OAuth2 code in the browser, prompting the end user to manually copy it to their app. It’s the client’s responsibility to either create a screen or a prompt to receive the code, then perform a code exchange for a token response.

When using the “out-of-browser” flow, an ID Token nonce is strongly recommended.

3 - Storage

Configuration options for persistent data storage

Dex requires persisting state to perform various tasks such as track refresh tokens, preventing replays, and rotating keys. This document is a summary of the storage configurations supported by dex.

Storage breaches are serious as they can affect applications that rely on dex. Dex saves sensitive data in its backing storage, including signing keys and bcrypt’d passwords. As such, transport security and database ACLs should both be used, no matter which storage option is chosen.

Etcd

Dex supports persisting state to etcd v3.

An example etcd configuration is using these values:

storage:
  type: etcd
  config:
    # list of etcd endpoints we should connect to
    endpoints:
      - http://localhost:2379
    namespace: my-etcd-namespace/

Etcd storage can be customized further using the following options:

  • endpoints: list of etcd endpoints we should connect to
  • namespace: etcd namespace to be set for the connection. All keys created by etcd storage will be prefixed with the namespace. This is useful when you share your etcd cluster amongst several applications. Another approach for setting namespace is to use etcd proxy
  • username: username for etcd authentication
  • password: password for etcd authentication
  • ssl: ssl setup for etcd connection
    • serverName: ensures that the certificate matches the given hostname the client is connecting to.
    • caFile: path to the ca
    • keyFile: path to the private key
    • certFile: path to the certificate

Kubernetes custom resource definitions (CRDs)

Kubernetes custom resource definitions are a way for applications to create new resources types in the Kubernetes API.

The Custom Resource Definition (CRD) API object was introduced in Kubernetes version 1.7 to replace the Third Party Resource (TPR) extension. CRDs allow dex to run on top of an existing Kubernetes cluster without the need for an external database. While this storage may not be appropriate for a large number of users, it’s extremely effective for many Kubernetes use cases.

The rest of this section will explore internal details of how dex uses CRDs. Admins should not interact with these resources directly, except while debugging. These resources are only designed to store state and aren’t meant to be consumed by end users. For modifying dex’s state dynamically see the API documentation.

The following is an example of the AuthCode resource managed by dex:

apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
  creationTimestamp: 2017-09-13T19:56:28Z
  name: authcodes.dex.coreos.com
  resourceVersion: "288893"
  selfLink: /apis/apiextensions.k8s.io/v1beta1/customresourcedefinitions/authcodes.dex.coreos.com
  uid: a1cb72dc-98bd-11e7-8f6a-02d13336a01e
spec:
  group: dex.coreos.com
  names:
    kind: AuthCode
    listKind: AuthCodeList
    plural: authcodes
    singular: authcode
  scope: Namespaced
  version: v1
status:
  acceptedNames:
    kind: AuthCode
    listKind: AuthCodeList
    plural: authcodes
    singular: authcode
  conditions:
  - lastTransitionTime: null
    message: no conflicts found
    reason: NoConflicts
    status: "True"
    type: NamesAccepted
  - lastTransitionTime: 2017-09-13T19:56:28Z
    message: the initial names have been accepted
    reason: InitialNamesAccepted
    status: "True"
    type: Established

Once the CustomResourceDefinition is created, custom resources can be created and stored at a namespace level. The CRD type and the custom resources can be queried, deleted, and edited like any other resource using kubectl.

dex requires access to the non-namespaced CustomResourceDefinition type. For example, clusters using RBAC authorization would need to create the following roles and bindings:

apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRole
metadata:
  name: dex
rules:
- apiGroups: ["dex.coreos.com"] # API group created by dex
  resources: ["*"]
  verbs: ["*"]
- apiGroups: ["apiextensions.k8s.io"]
  resources: ["customresourcedefinitions"]
  verbs: ["create"] # To manage its own resources identity must be able to create customresourcedefinitions.
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
  name: dex
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: dex
subjects:
- kind: ServiceAccount
  name: dex                 # Service account assigned to the dex pod.
  namespace: dex-namespace  # The namespace dex is running in.

Removed: Kubernetes third party resources(TPRs)

TPR support in dex has been removed. The last version to support TPR is v2.17.0

If you are currently running dex using TPRs, you will need to migrate to CRDs before you upgrade to a post v2.17 dex. The script mentioned in the instructions can be found here

Configuration

The storage configuration is extremely limited since installations running outside a Kubernetes cluster would likely prefer a different storage option. An example configuration for dex running inside Kubernetes:

storage:
  type: kubernetes
  config:
    inCluster: true

Dex determines the namespace it’s running in by parsing the service account token automatically mounted into its pod.

SQL

Dex supports three flavors of SQL: SQLite3, Postgres and MySQL.

Migrations are performed automatically on the first connection to the SQL server (it does not support rolling back). Because of this dex requires privileges to add and alter the tables for its database.

NOTE: Previous versions of dex required symmetric keys to encrypt certain values before sending them to the database. This feature has not yet been ported to dex v2. If it is added later there may not be a migration path for current v2 users.

SQLite3

SQLite3 is the recommended storage for users who want to stand up dex quickly. It is not appropriate for real workloads.

The SQLite3 configuration takes a single argument, the database file.

storage:
  type: sqlite3
  config:
    file: /var/dex/dex.db

Because SQLite3 uses file locks to prevent race conditions, if the “:memory:” value is provided dex will automatically disable support for concurrent database queries.

Postgres

When using Postgres, admins may want to dedicate a database to dex for the following reasons:

  1. Dex requires privileged access to its database because it performs migrations.
  2. Dex’s database table names are not configurable; when shared with other applications there may be table name clashes.
CREATE DATABASE dex_db;
CREATE USER dex WITH PASSWORD '66964843358242dbaaa7778d8477c288';
GRANT ALL PRIVILEGES ON DATABASE dex_db TO dex;

An example config for Postgres setup using these values:

storage:
  type: postgres
  config:
    host: localhost
    port: 5432
    database: dex_db
    user: dex
    password: 66964843358242dbaaa7778d8477c288
    ssl:
      mode: verify-ca
      caFile: /etc/dex/postgres.ca

The SSL “mode” corresponds to the github.com/lib/pq package connection options. If unspecified, dex defaults to the strictest mode “verify-full”.

MySQL

Dex requires MySQL 5.7 or later version. When using MySQL, admins may want to dedicate a database to dex for the following reasons:

  1. Dex requires privileged access to its database because it performs migrations.
  2. Dex’s database table names are not configurable; when shared with other applications there may be table name clashes.
CREATE DATABASE dex_db;
CREATE USER dex IDENTIFIED BY '66964843358242dbaaa7778d8477c288';
GRANT ALL PRIVILEGES ON dex_db.* TO dex;

An example config for MySQL setup using these values:

storage:
  type: mysql
  config:
    database: dex_db
    user: dex
    password: 66964843358242dbaaa7778d8477c288
    ssl:
      mode: custom
      caFile: /etc/dex/mysql.ca

The SSL “mode” corresponds to the github.com/go-sql-driver/mysql package connection options. If unspecified, dex defaults to the strictest mode “true”.

Adding a new storage options

Each storage implementation bears a large ongoing maintenance cost and needs to be updated every time a feature requires storing a new type. Bugs often require in depth knowledge of the backing software, and much of this work will be done by developers who are not the original author. Changes to dex which add new storage implementations require a strong use case to be considered for inclusion.

New storage option references

Those who still want to construct a proposal for a new storage should review the following packages:

  • github.com/dexidp/dex/storage: Interface definitions which the storage must implement. NOTE: This package is not stable.
  • github.com/dexidp/dex/storage/conformance: Conformance tests which storage implementations must pass.

New storage option requirements

Any proposal to add a new implementation must address the following:

  • Integration testing setups (Travis and developer workstations).
  • Transactional requirements: atomic deletes, updates, etc.
  • Is there an established and reasonable Go client?

4 - OAuth2

OAuth2 flow customization options

Dex provides a range of configurable options that empower you to fine-tune and personalize various aspects of the authentication and user flow.

Flow Customization

Customize OAuth2 settings to align with your authentication requirements.

oauth2:
  responseTypes: [ "code" ]
  skipApprovalScreen: true
  alwaysShowLoginScreen: false

Authentication flow

  • responseTypes - allows you to configure the desired auth flow (Authorization Code Flow, Implicit Flow, or Hybrid Flow) based on different values. See the table below for valid configuration options.
responseTypes valueflow
codeAuthorization Code Flow
id_tokenImplicit Flow
id_token tokenImplicit Flow
code id_tokenHybrid Flow
code tokenHybrid Flow
code id_token tokenHybrid Flow
Examples of the different flows and their behavior can be found in the official openid spec.

User flow

Customizing the user flow allows you to influence how users login into your application.

  • skipApprovalScreen - controls the need for user approval before sharing data with connected applications. If enabled, users must approve data sharing with every auth flow.
  • alwaysShowLoginScreen - whether to always display the login screen. If only one authentication method is enabled, the default behavior is to go directly to it. For connected IdPs, this redirects the browser away from the application to upstream provider, such as the Google login page.

Password grants

Password grants involve clients directly sending a user’s credentials (username and password) to the authorization server (dex), acquiring access tokens without the need for an intermediate authorization step.

oauth2:
  passwordConnector: local
  • passwordConnector - specifies the connector’s id that is used for password grants

WARNING: The password grant type is not recommended for use by the OAuth 2.0 Security Best Current Practice because of serious security concerns. Please see oauth.net for additional information.

5 - The Dex API

Configure Dex dynamically with the gRPC API

Dex provides a gRPC service for programmatic modification of dex’s state. The API is intended to expose hooks for management applications and is not expected to be used by most installations.

This document is an overview of how to interact with the API.

Configuration

Admins that wish to expose the gRPC service must add the following entry to the dex config file. This option is off by default.

grpc:
  # Cannot be the same address as an HTTP(S) service.
  addr: 127.0.0.1:5557

  # Server certs. If TLS credentials aren't provided dex will run in plaintext (HTTP) mode.
  tlsCert: /etc/dex/grpc.crt
  tlsKey: /etc/dex/grpc.key

  # Client auth CA.
  tlsClientCA: /etc/dex/client.crt

  # enable reflection
  reflection: true

Clients

gRPC is a suite of tools for generating client and server bindings from a common declarative language. The canonical schema for Dex’s API can be found in the source tree at api/v2/api.proto. Go bindings are generated and maintained in the same directory for both public and internal use.

Go

A Go project can import the API module directly, without having to import the entire project:

go get github.com/dexidp/dex/api/v2

The client then can be used as follows:

package main

import (
    "context"
    "fmt"
    "log"

    "github.com/dexidp/dex/api/v2"
    "google.golang.org/grpc"
    "google.golang.org/grpc/credentials"
)

func newDexClient(hostAndPort, caPath string) (api.DexClient, error) {
    creds, err := credentials.NewClientTLSFromFile(caPath, "")
    if err != nil {
        return nil, fmt.Errorf("load dex cert: %v", err)
    }

    conn, err := grpc.Dial(hostAndPort, grpc.WithTransportCredentials(creds))
    if err != nil {
        return nil, fmt.Errorf("dial: %v", err)
    }
    return api.NewDexClient(conn), nil
}

func main() {
    client, err := newDexClient("127.0.0.1:5557", "/etc/dex/grpc.crt")
    if err != nil {
        log.Fatalf("failed creating dex client: %v ", err)
    }

    req := &api.CreateClientReq{
        Client: &api.Client{
            Id:           "example-app",
            Name:         "Example App",
            Secret:       "ZXhhbXBsZS1hcHAtc2VjcmV0",
            RedirectUris: []string{"http://127.0.0.1:5555/callback"},
        },
    }

    if _, err := client.CreateClient(context.TODO(), req); err != nil {
        log.Fatalf("failed creating oauth2 client: %v", err)
    }
}

A clear working example of the Dex gRPC client for Go can be found here.

Other languages

To generate a client for your own project install protoc, install a protobuf generator for your project’s language, and download the api.proto file.

Here is an example:

# Download api.proto for a given version.
$ DEX_VERSION=v2.24.0
$ wget https://raw.githubusercontent.com/dexidp/dex/${DEX_VERSION}/api/v2/api.proto

# Generate the client bindings.
$ protoc [YOUR LANG PARAMS] api.proto

Client programs can then be written using the generated code.

Authentication and access control

The Dex API does not provide any authentication or authorization beyond TLS client auth.

Projects that wish to add access controls on top of the existing API should build apps which perform such checks. For example to provide a “Change password” screen, a client app could use Dex’s OpenID Connect flow to authenticate an end user, then call Dex’s API to update that user’s password.

dexctl?

Dex does not ship with a command line tool for interacting with the API. Command line tools are useful but hard to version, easy to design poorly, and expose another interface which can never be changed in the name of compatibility.

While the Dex team would be open to re-implementing dexctl for v2 a majority of the work is writing a design document, not the actual programming effort.

Why not REST or gRPC Gateway?

Between v1 and v2, Dex switched from REST to gRPC. This largely stemmed from problems generating documentation, client bindings, and server frameworks that adequately expressed REST semantics. While Google APIs, Open API/Swagger, and gRPC Gateway were evaluated, they often became clunky when trying to use specific HTTP error codes or complex request bodies. As a result, v2’s API is entirely gRPC.

Many arguments against gRPC cite short term convenience rather than production use cases. Though this is a recognized shortcoming, Dex already implements many features for developer convenience. For instance, users who wish to manually edit clients during testing can use the staticClients config field instead of the API.