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

Return to the regular view of this page.

Development

Dev Environment Setup, Testing, and Contributing to Dex

This section explains how you can develop for the Dex project and documents what steps need to be done at each phase of development (setting up the development environment, running tests, submitting changes, releasing software, etc).

Dex is an open source software (licensed under the Apache-2.0 license) and contributions (of any kind) are more than welcome from anyone in the community.

1 - OpenID Connect Provider Certification

The OpenID Foundation provides a set of conformance test profiles that test both Relying Party (RP) and OpenID Provider (OP) OpenID Connect implementations. Upon submission of results and an affirmative response, the affirmed OP will be listed as a certified OP on the OpenID Connect website and allowed to use the certification mark according to the certification terms and conditions, section 3(d).

Further details about the certification are available on the OpenID Connect website.

Basic OpenID Provider profile

Dex is an OP that strives to implement the mandatory set of OpenID Connect features, and can be tested against the Basic OpenID Provider profile (profile outline, section 2.1.1). These tests ensure that all features required by a basic client work as expected.

Unfortunately, Dex currently does not fully comply with the Basic profile at the moment.

The progress for getting Dex certified can be tracked here: https://github.com/orgs/dexidp/projects/3/views/1

Configuring Dex

The Basic OP test suite doesn’t require extensive configuration from Dex. The suite needs the following:

  • A public issuer URL
  • At least two separate clients (with redirect URIs pointing to https://www.certification.openid.net/test/a/YOUR_ALIAS/callback).

YOUR_ALIAS is an arbitrary string that MUST be unique to avoid interference with other test runs.

The easiest way to run a public Dex instance is running one locally and exposing it using a tunnel.

The following instructions use tunnelto.dev.

Here is a minimal configuration example for running Dex:

issuer: https://dex.tunnelto.dev/dex

storage:
  type: memory

web:
  http: 0.0.0.0:5556

oauth2:
  # Automate some clicking
  # Note: this might actually make some tests pass that otherwise wouldn't.
  skipApprovalScreen: true

connectors:
  # Note: this might actually make some tests pass that otherwise wouldn't.
  - type: mockCallback
    id: mock
    name: Example

# Basic OP test suite requires two clients.
staticClients:
  - id: first_client
    secret: 89d6205220381728e85c4cf5
    redirectURIs:
      - https://www.certification.openid.net/test/a/dex/callback
    name: First client

  - id: second_client
    secret: 51c612288018fd384b05d6ad
    redirectURIs:
      - https://www.certification.openid.net/test/a/dex/callback
    name: Second client

Save it in a file (eg. config.yaml) then launch Dex:

dex serve config.yaml

Then launch the tunnel:

tunnelto --subdomain dex --port 5556

You can verify Dex running by checking the discovery endpoint:

curl https://dex.tunnelto.dev/dex/.well-known/openid-configuration

Running tests

  1. Open https://www.certification.openid.net/ in your browser
  2. Login with your Google or GitLab account
  3. Click Create a new test plan
  4. Select OpenID Connect Core: Basic Certification Profile Authorization server test as the test plan
  5. Server metadata location should be discovery
  6. Client registration type should be static_client
  7. Choose an alias (that you used in the redirect URIs above)
  8. Enter the discovery URL
  9. Enter the first client details in the Client and Second client sections
  10. Enter the second client details in the Client for client_secret_post section
  11. Hit Create test plan
  12. Run through each test case, following all instructions given by individual cases.
    • In order to pass certain cases, screenshots of OP responses might be required.

Last results

Dex does not fully pass the Basic profile test suite yet. The following table contains the current state of test results.

Test NameStatusResult
oidcc-serverFINISHEDPASSED
oidcc-response-type-missingFINISHEDPASSED
oidcc-userinfo-getFINISHEDPASSED
oidcc-userinfo-post-headerFINISHEDPASSED
oidcc-userinfo-post-bodyFINISHEDWARNING
oidcc-ensure-request-without-nonce-succeeds-for-code-flowFINISHEDPASSED
oidcc-scope-profileFINISHEDWARNING
oidcc-scope-emailFINISHEDWARNING
oidcc-scope-addressFINISHEDSKIPPED
oidcc-scope-phoneFINISHEDSKIPPED
oidcc-scope-allFINISHEDSKIPPED
oidcc-ensure-other-scope-order-succeedsFINISHEDWARNING
oidcc-display-pageFINISHEDPASSED
oidcc-display-popupFINISHEDPASSED
oidcc-prompt-loginINTERRUPTEDUNKNOWN
oidcc-prompt-none-not-logged-inFINISHEDFAILED
oidcc-prompt-none-logged-inFINISHEDPASSED
oidcc-max-age-1INTERRUPTEDFAILED
oidcc-max-age-10000FINISHEDFAILED
oidcc-ensure-request-with-unknown-parameter-succeedsFINISHEDPASSED
oidcc-id-token-hintFINISHEDPASSED
oidcc-login-hintFINISHEDPASSED
oidcc-ui-localesFINISHEDPASSED
oidcc-claims-localesFINISHEDPASSED
oidcc-ensure-request-with-acr-values-succeedsFINISHEDWARNING
oidcc-codereuseFINISHEDPASSED
oidcc-codereuse-30secondsFINISHEDWARNING
oidcc-ensure-registered-redirect-uriINTERRUPTEDREVIEW
oidcc-server-client-secret-postFINISHEDPASSED
oidcc-unsigned-request-object-supported-correctly-or-rejected-as-unsupportedINTERRUPTEDUNKNOWN
oidcc-claims-essentialFINISHEDWARNING
oidcc-ensure-request-object-with-redirect-uriINTERRUPTEDUNKNOWN
oidcc-refresh-tokenINTERRUPTEDFAILED
oidcc-ensure-request-with-valid-pkce-succeedsFINISHEDPASSED

TODO: find a better place for test results.

2 - Releases

Releasing a new version of Dex can be done by one of the core maintainers with push access to the git repository. It’s usually good to have an extra pair of eyes ready when tagging a new release though, so feel free to ask a peer to be ready in case anything goes wrong or you need a review.

The release process is semi-automated at the moment: artifacts are automatically built and published to GitHub Container Registry (primary source of container images) and Docker Hub.

The GitHub release needs to be manually created (use past releases as templates).

Note: this will hopefully be improved in the future.

Tagging a new release

Make sure you’ve uploaded your GPG key and configured git to use that signing key either globally or for the Dex repo. Note that the email the key is issued for must be the email you use for git.

git config [--global] user.signingkey "{{ GPG key ID }}"
git config [--global] user.email "{{ Email associated with key }}"

Create a signed tag at the commit you wish to release.

RELEASE_VERSION=v2.0.0
git tag -s -m "Release $RELEASE_VERSION" $RELEASE_VERSION # optionally: commit hash as the last argument

Push that tag to the Dex repo.

git push origin $RELEASE_VERSION

Draft releases on GitHub and summarize the changes since the last release. See previous releases for the expected format.

Patch releases

Occasionally, patch releases might be necessary to fix an urgent bug or vulnerability.

First, check if there is a release branch for a minor release. Create one if necessary:

MINOR_RELEASE="v2.1.0"
RELEASE_BRANCH="v2.1.x"
git checkout -b $RELEASE_BRANCH tags/$MINOR_RELEASE
git push origin $RELEASE_BRANCH

If a patch version is needed (2.1.1, 2.1.2, etc.), checkout the desired release branch and cherry pick specific commits.

RELEASE_BRANCH="v2.1.x"
git checkout $RELEASE_BRANCH
git checkout -b "cherry-picked-change"
git cherry-pick (SHA of change)
git push origin "cherry-picked-change"

Open a PR onto $RELEASE_BRANCH to get the changes approved.

Continue with the regular release process.

Dex API

If there are changes in the API, the API version should be bumped to appear correctly in the pkg.go.dev and be able to be pulled by tags (via go get or go modules).

Create a new tag with the api/ path:

RELEASE_VERSION=v2.3.0
git tag -s -m "${RELEASE_VERSION} Dex API release" "api/${RELEASE_VERSION}"

Push that tag to the Dex repo.

git push origin "api/${RELEASE_VERSION}"

3 - Running Integration Tests

Postgres

Running database tests locally requires:

  • Docker

To run the database integration tests:

  • start a postgres container:

    docker run --name dex-postgres -e POSTGRES_USER=postgres -e POSTGRES_DB=dex -p 5432:5432 -d postgres:11
    
  • export the required environment variables:

    export DEX_POSTGRES_DATABASE=dex DEX_POSTGRES_USER=postgres DEX_POSTGRES_PASSWORD=postgres DEX_POSTGRES_HOST=127.0.0.1:5432
    
  • run the storage/sql tests:

    $ # sqlite3 takes forever to compile, be sure to install test dependencies
    $ go test -v -i ./storage/sql
    $ go test -v ./storage/sql
    
  • clean up the postgres container:

    docker rm -f dex-postgres
    

Etcd

These tests can also be executed using docker:

  • start the container (where NODE1 is set to the host IP address):

    $ export NODE1=0.0.0.0
    $ docker run --name dex-etcd -p 2379:2379 -p 2380:2380 gcr.io/etcd-development/etcd:v3.3.10 \
      /usr/local/bin/etcd --name node1 \
      --initial-advertise-peer-urls http://${NODE1}:2380 --listen-peer-urls http://${NODE1}:2380 \
      --advertise-client-urls http://${NODE1}:2379 --listen-client-urls http://${NODE1}:2379 \
      --initial-cluster node1=http://${NODE1}:2380
    
  • run the tests, passing the correct endpoint for this etcd instance in DEX_ETCD_ENDPOINTS:

    DEX_ETCD_ENDPOINTS=http://localhost:2379 go test -v ./storage/etcd
    
  • clean up the etcd container: docker rm -f dex-etcd

Kubernetes

Running integration tests for Kubernetes storage requires the DEX_KUBERNETES_CONFIG_PATH environment variable be set with the path to kubeconfig file of the existing cluster. For tests, it is ok to use “mini” Kubernetes distributive, e.g., KinD, Microk8s.

Example KinD cluster test run:

  • Install KinD using the instructions from the official website.

  • Run tests by executing the following commands:

    export DEX_KUBERNETES_CONFIG_PATH=$(mktemp /tmp/kubeconfig.XXXXXXXX)
    kind create cluster --kubeconfig "$DEX_KUBERNETES_CONFIG_PATH"
    
    go test -v ./storage/kubernetes
    
  • To clean up, run:

    rm -f "$DEX_KUBERNETES_CONFIG_PATH"
    unset DEX_KUBERNETES_CONFIG_PATH
    
    kind delete cluster
    

LDAP

The LDAP integration tests require OpenLDAP installed on the host machine. To run them, use go test:

export DEX_LDAP_TESTS=1
go test -v ./connector/ldap/

To quickly stand up a LDAP server for development, see the LDAP “Getting started” example. This also requires OpenLDAP installed on the host.

To stand up a containerized LDAP server run the OpenLDAP docker image:

$ sudo docker run --hostname ldap.example.org --name openldap-container --detach osixia/openldap:1.1.6

By default TLS is enabled and a certificate is created with the container hostname, which in this case is “ldap.example.org”. It will create an empty LDAP for the company Example Inc. and the domain example.org. By default the admin has the password admin.

Add new users and groups (sample .ldif file included at the end):

$ sudo docker exec openldap-container ldapadd -x -D "cn=admin,dc=example,dc=org" -w admin -f <path to .ldif> -h ldap.example.org -ZZ

Verify that the added entries are in your directory with ldapsearch :

$ sudo docker exec openldap-container ldapsearch -x -h localhost -b dc=example,dc=org -D "cn=admin,dc=example,dc=org" -w admin

The .ldif file should contain seed data. Example file contents:

dn: cn=Test1,dc=example,dc=org
objectClass: organizationalRole
cn: Test1

dn: cn=Test2,dc=example,dc=org
objectClass: organizationalRole
cn: Test2

dn: ou=groups,dc=example,dc=org
ou: groups
objectClass: top
objectClass: organizationalUnit

dn: cn=tstgrp,ou=groups,dc=example,dc=org
objectClass: top
objectClass: groupOfNames
member: cn=Test1,dc=example,dc=org
cn: tstgrp

SAML

Okta

The Okta identity provider supports free accounts for developers to test their implementation against. This document describes configuring an Okta application to test dex’s SAML connector.

First, sign up for a developer account. Then, to create a SAML application:

  • Go to the admin screen.
  • Click “Add application”
  • Click “Create New App”
  • Choose “SAML 2.0” and press “Create”
  • Configure SAML
    • Enter http://127.0.0.1:5556/dex/callback for “Single sign on URL”
    • Enter http://127.0.0.1:5556/dex/callback for “Audience URI (SP Entity ID)”
    • Under “ATTRIBUTE STATEMENTS (OPTIONAL)” add an “email” and “name” attribute. The values should be something like user:email and user:firstName, respectively.
    • Under “GROUP ATTRIBUTE STATEMENTS (OPTIONAL)” add a “groups” attribute. Use the “Regexp” filter .*.

After the application’s created, assign yourself to the app.

  • “Applications” > “Applications”
  • Click on your application then under the “People” tab press the “Assign to People” button and add yourself.

At the app, go to the “Sign On” tab and then click “View Setup Instructions”. Use those values to fill out the following connector in examples/config-dev.yaml.

connectors:
- type: saml
  id: saml
  name: Okta
  config:
    ssoURL: ( "Identity Provider Single Sign-On URL" )
    caData: ( base64'd value of "X.509 Certificate" )
    redirectURI: http://127.0.0.1:5556/dex/callback
    usernameAttr: name
    emailAttr: email
    groupsAttr: groups

Start both dex and the example app, and try logging in (requires not requesting a refresh token).