Best practice for giving third party users API Access

Hello,
I have spent a lot of time playing with keycloak, and also reading other posts about Architecting API access for third party users and decided to aggregate these approaches here since I still haven’t found a great approach, and it doesnt seem like anyone else has either.

VERSION: I am running keycloak 12.04 on kubernetes (we have multiple environments both locally and in EKS)

Approach #1: Creating a lot of API Clients
An API client is created in keycloak for each end user API. The client-id and client-secret are returned to the and-user and they are able to retrieve an access-token via keycloak token endpoint. When a user wants to revoke access, the API-client would be deleted.
Pros:

  • This seems to be “the most correct” and straight forward approach from an oauth2 perspective
  • It’s simple!
    Cons:
  • Scaleability: if you have 10,000 users, and each one has at least one API-client, keycloak needs to handle 10000+ API-clients!? What about 100,000!? Can keycloak handle this many clients? The answer seems to be maybe: https://issues.redhat.com/browse/KEYCLOAK-8275
    – I did some trials myself, and created over 1000 clients in a test-realm. This worked, but the UI was sluggish and deleting the realm failed via timeout (presumably because it took longer than 5 minutes to delete all the API-clients)
  • Organization. I would prefer to have a realm dedicated to third-party API-clients, since there will be so many and they would pollute my regular business logic realms, BUT this requires duplication of any custom roles, resources, permissions, and policies that I use in my main realm.

Approach #2: Use Keycloak Sessions and set the SSO Session Max and SSO Session Idle and the client timeouts to be REALLY BIG. Like 50 years big!
Using the admin rest API, a token exchange can be performed to create user access and refresh tokens against a single API-client for all third-party users. The refresh token is returned to the end user, who can use it as a common api-token/api-secret to retrieve a short-lived access-token anytime API access is needed. Since this approach treats refresh_tokens as api-secrets the refresh_token would be expected to last indefinitely (or at least a very long time before expiring), which is why we make the sso session max and sso session idle super big.
Pros:

  • Sessions are created and tracked in keycloak. These sessions can be created and destroyed and viewed to the end user as an API client!
    Cons:
  • In my experience, anytime i set the sso session max greater than 5 years, keycloak would quietly reject any logins. I found some bugs describing similar things, but they are old (like 2017 or 2019 old)
  • Sessions are all destroyed when keycloak is restarted. This deems this approach a complete non-starter :frowning: UNLESS you want to host a dedicated infinispan cache (our team does not want to)

Approach #3: Use offline tokens
This approach is identical to approach number 2, except instead of retrieving regular refresh_token, you would request an offline token from keycloak. An offline token is simply a refresh_token that isn’t bound to a session and thusly not bound by the so offline settings, and can also survive restarts. This special refresh_token would be returned to the end-user.
Offline token expiry is controlled via offline session max and offline session idle (and the respective client overrides) so if you make these really big, the offline tokens wont expire for a really long time!

Pros:

  • You get refresh tokens which can be used for a very long time!
  • These tokens (unlike the regular refresh tokens in approach # 2) will survive a restart!
    Cons:
  • There is no way to revoke a single offline token. They can only be batch deleted for a single user, or a client. As far as i know. I would love to learn otherwise :pray:
    – If you plan to offer the ability for an end user to create more than one API token with the ability of revoking one while keeping the others active, this approach won’t work :frowning:

Approach #4: DIY! (but only a little)
Create and store ‘client-id’ and ‘client-secrets’ in a custom database, and multiplex these clients to a single keycloak api client. The client-id and client-secret for the actual keycloak api client will not be known to the end users, instead the end users will be provided a client-id and client-secret generated by your own custom application.
When a user attempts to retrieve an access token, they will perform the client_credentials oauth2 grant flow with a custom token endpoint. This endpoint will verify the client-id/client-secret match in the custom db, get the keycloak userID from custom db, and finally perform a token-exchange against the single api-client in keycloak for the specified user. This access-token would then be returned to the user and could be used to access API endpoints.

Pros

  • Scales very well
  • doesn’t clutter up keycloak
    Cons
  • custom solution that duplicates a bit of the functionality that using keycloak should solve in the first place
  • Somewhat against the oauth2 spec… the issuer (iss) of the token does not match the token-endpoint used to receive the token. It’s possible strict oauth2 implementations might raise might raise some eyebrows at this.

We are leaning towards option 4… but I am hoping someone can save us from this madness!

Kind regards,
Ben

1 Like