Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add OpenID-Connect Support #78

Merged
merged 42 commits into from
Apr 10, 2020

Conversation

nscuro
Copy link
Collaborator

@nscuro nscuro commented Feb 16, 2020

This PR addresses #10 by implementing support for OAuth2 / OpenID Connect.

I'm opening this PR in a WIP state mainly for transparency reasons, but also to potentially get early feedback. I'll update this description when something changes and let you know when I feel like the PR is ready for a serious review.

TODO

  • Test with multiple authorization servers
  • Unit test coverage

How to test

Besides the unit tests, here's a quick guide on how to test this implementation in action. It can be done locally using Docker, Keycloak and Dependency-Track:

  • Checkout this branch and install the Maven project:
$ git clone -b 10-openidconnect-support https://github.com/nscuro/Alpine.git
$ cd Alpine
$ mvn clean clean install -Dcyclonedx.skip -DskipTests

Setup Dependency-Track

  • Checkout the openidconnect-support branch of Dependency-Track and build an executable WAR:
$ git clone -b openidconnect-support https://github.com/nscuro/dependency-track.git
$ cd dependency-track
$ mvn clean package -Dcyclonedx.skip -DskipTests -P embedded-jetty -Dlogback.configuration.file=src/main/docker/logback.xml
  • Start Dependency-Track with OpenID Connect enabled, including its user provisioning and team synchronization capabilities:
$ java -Dalpine.oidc.enabled=true -Dalpine.oidc.issuer=http://localhost:8081/auth/realms/dtrack-oidc -Dalpine.oidc.username.claim=preferred_username -Dalpine.oidc.user.provisioning=true -Dalpine.oidc.team.synchronization=true -Dalpine.oidc.teams.claim=groups -jar target/dependency-track-embedded.war
  • Checkout the openidconnect-support branch of the new Dependency-Track frontend:
$ git clone -b openidconnect-support https://github.com/nscuro/frontend.git
  • Modify public/static/config.json as follows:
"OIDC_ISSUER": "http://localhost:8081/auth/realms/dtrack-oidc",
"OIDC_CLIENT_ID": "dependency-track",
"OIDC_FLOW": "code"
  • Run the development server: npm run serve
  • Navigate to the server's base url and login with the default Dependency-Track admin account (admin:admin)
  • Navigate to Administration -> Access Management -> OpenID Connect Groups and add create the groups DTRACK_ADMINS and DTRACK_USERS
  • Map DTRACK_ADMINS to the team Administrators and DTRACK_USERS to Portfolio Managers

Setup Keycloak

  • Download realm-export.json:
    • Includes the default configuration of the master realm with pre-configured client dependency-track
$ curl -o realm-export.json https://gist.githubusercontent.com/nscuro/70949d507207cd27d2dce5d8e72fff45/raw/ebfbc12c2e9ce46845ada708554e124b0cf76e4d/realm-export.json
  • Start Keycloak, importing realm-export.json
$ docker run -d -e KEYCLOAK_USER=admin -e KEYCLOAK_PASSWORD=admin -e KEYCLOAK_IMPORT=/tmp/realm-export.json -v "$(pwd)/realm-export.json:/tmp/realm-export.json" -p 8081:8080 --name keycloak jboss/keycloak:8.0.2
  • Login to Keycloak at http://localhost:8081/auth with admin:admin
  • Select the realm dtrack-oidc
  • Add a new user under Manage-> Users -> Add user with the following credentials:
    • Username: dtrack
    • E-Mail: dtrack@mail.local
    • Password: test (Make sure the option Temporary is OFF)
  • After creation, navigate to the Groups tab for the user and join either DTRACK_ADMINS, DTRACK_USERS or both

Test

  • Download the *.postman_collection.json and *.postman_environment.json files from this gist and import them into Postman
  • Send the Keycloak: Get Access Token request to acquire an access token for the user dtrack:
    Keycloak: Get Access Token
  • Send the DTrack: OIDC Login request to exchange the Keycloak access token for a JWT issued by Dependency-Track:
    DTrack: OIDC Login
  • Finally, verify that the user has been privisioned and the teams have been synced by sending the DTrack: Get Self request:
    DTrack: Get Self

Thanks to Vinod for pointing out that this might be a better solution with Dependency-Track's frontend being rewritten as SPA
This is required for tests of classes that make use of the cache
…point

Also, remove OidcSigningKeyResolver and remove OidcAuthenticationService from AuthenticationFilter. These changes are necessary for compatibility with Auth Servers that do not provide their Access Tokens in JWT form. GitLab for example just uses random strings. Additionally, we'll authenticate via OIDC just once now and then issue a "local" JWT. This is the way LDAP authentication is implemented as well.
Unlike LDAP, the OpenID Connect standard does not provide any means to retrieve a list of all available groups or roles. In order to still allow for a mapping between OIDC groups/roles and Teams, Admins will need to setup groups in Alpine manually.
Conveniently implicitly contains tests for getters & setters
@nscuro nscuro changed the title (WIP) Add OpenID-Connect Support Add OpenID-Connect Support Mar 16, 2020
@nscuro
Copy link
Collaborator Author

nscuro commented Mar 16, 2020

@stevespringett I'd consider this PR to be ready for review. I'll possibly add minor changes while integrating this into Dependency-Track and its new frontend, but all in all this is pretty much the final version.

This is to be uniform with the frontend configuration. JavaScript's oidc-client (currently) does not allow to directly provide the discovery uri.
https://openid.net/specs/openid-connect-discovery-1_0.html states: "URL using the https scheme with no query or fragment component that the OP asserts as its Issuer Identifier. If Issuer discovery is supported (see Section 2), this value MUST be identical to the issuer value returned by WebFinger. This also MUST be identical to the iss Claim value in ID Tokens issued from this Issuer."
@nscuro
Copy link
Collaborator Author

nscuro commented Mar 23, 2020

I noticed that the current way of authenticating users is somewhat flawed. LDAP or OIDC users with the name admin are able to login as the managed user admin. JWTs issued by Alpine / Dependency-Track only contain the username, which is not guaranteed to be unique across all identity providers. Alpine attempts to load users by their name in the order Managed -> LDAP -> OIDC. We probably need another claim in the JWT that denotes the identity provider that was used for authentication.

I was thinking about a new claim idp. When issuing JWTs, Dependency-Track can then specify which identity provider performed the authentication. So options could be LOCAL, LDAP, OIDC for example. JwtAuthenticationService would then evaluate the idp claim and only attempt to load the user from the correlating database tables.

This is for the use case where admins manually create OidcUsers. At this time, only the username is known. Now, if a user that has been manually created logs in for the first time, his subject identifier is assigned to the OidcUser entity. From this point on, the subject identifier cannot change. If it does, authentication will fail.
@stevespringett stevespringett merged commit 9901c25 into stevespringett:master Apr 10, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants