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

OAuth 2.0 Token Exchange (RFC 8693) #437

Open
guicassolato opened this issue Oct 17, 2023 · 2 comments
Open

OAuth 2.0 Token Exchange (RFC 8693) #437

guicassolato opened this issue Oct 17, 2023 · 2 comments

Comments

@guicassolato
Copy link
Collaborator

guicassolato commented Oct 17, 2023

Authorino does not provide any built-in functionality for obtaining OAuth2 access tokens on behalf of clients trying to access a service that is protected by an AuthConfig. This is by design to not violate a principle of the OAuth2 protocol, apart from a good practice of application security in general, according which each client is responsible for managing its own access tokens and credentials.1

Users of Authorino nevertheless often look out for ways of leveraging the flexibility provided by the authorization service, to handle the flows for obtaining and the management of access tokens. A lightweight example of this, that may not involve storing client authentication secrets in the AuthConfig, is relying on built-in OPA support or HTTP GET/GET-by-POST metadata to simply refresh an access token previously obtained by the client by its own means.

Example: OAuth2 token refresh performed by Authorino

Follow the steps ① to ④ of the OpenID Connect Discovery and authentication with JWTs user guide. Then, create the following AuthConfig that uses the refresh token passed in a Request-Token HTTP request header to exchange it on behalf of the client for a new access token.

  apiVersion: authorino.kuadrant.io/v1beta2
kind: AuthConfig
metadata:
  name: refresh-token
spec:
  hosts:
  - talker-api.127.0.0.1.nip.io
  authentication:
    "keycloak-users":
      jwt:
        issuerUrl: http://keycloak.keycloak.svc.cluster.local:8080/auth/realms/kuadrant
    "anonymous-access": # enables anonymous access when it fails to validate the jwt and a refresh token is present
      when:
      - selector: request.headers.refresh-token
        operator: neq
        value: ""
      anonymous: {}
      priority: 1
  metadata:
    "refresh-token": # tries to refresh the token when it's anonymous access
      when:
      - selector: auth.identity.anonymous
        operator: eq
        value: "true"
      http:
        url: http://keycloak.keycloak.svc.cluster.local:8080/auth/realms/kuadrant/protocol/openid-connect/token
        method: POST
        bodyParameters:
          "grant_type":
            value: refresh_token
          "client_id":
            value: demo
          "refresh_token":
            selector: request.headers.refresh-token
  authorization:
    "valid-token": # ensures a valid token is present, either by checking it is not anonymous access or because it succeeded refreshing the token
      when:
      patternMatching:
        patterns:
        - any:
          - selector: auth.identity.anonymous
            operator: neq
            value: "true"
          - selector: auth.metadata.refresh-token.access_token
            operator: neq
            value: ""
  response:
    success:
      headers:
        "authorization": # replaces the authorization header with the new token if obtained
          when:
          - selector: auth.metadata.refresh-token.access_token
            operator: neq
            value: ""
          plain:
            selector: "Bearer {auth.metadata.refresh-token.access_token}"
        "refresh-token": # replaces the refresh-token header with the new refresh token if obtained
          when:
          - selector: auth.metadata.refresh-token.refresh_token
            operator: neq
            value: ""
          plain:
            selector: auth.metadata.refresh-token.refresh_token

Instead of handling the OAuth2 grant/OIDC flows on behalf of the clients – a functionality more directly provided by solutions such as OAuth2 Proxy and Envoy's OAuth2 filter – Authorino implemented its own built-in Security Token Service (STS), called Festival Wristband.

Clients can exchange tokens – including tokens not limited to OIDC-issued JWTs (OAuth2 opaque tokens, API keys, etc) – for a highly customizable new JWT that the Authorino instance itself provides the keys to verify. This solution is particularly useful for implementing Edge Authentication Architecture (EAE) and applications in general that require reducing the scope of access tokens, avoid sharing tokens across services, etc.

Although it doesn't replace the need by the clients to handle the initial OIDC flow when that is the method of choice for authentication, Authorino's Festival Wristband tokens do provide an alternative to managing and to refreshing secondary tokens issued under the new scope. On the flip side, Authorino loses transparency in the identity management flow and becomes a token issuer authority.

Aiming to delegate the responsibility of issuing the wristband tokens to a proper authentication server and thus avoiding Authorino becoming a central actor in the identity management flow, this feature was proposed to be extended.

Since the OAuth 2.0 Token Exchange protocol (RFC 8693) became a standard, it opened up new possibilities for the implementation of the aforementioned proposal – if not to fully replace the feature.

One important aspect of OAuth 2.0 Token Exchange though is its built-in support for impersonation, which means, if adopted, that Authorino would lose transparency in the process of obtaining the new token, but (i) without becoming a central piece of the identity management flow – i.e., without becoming a token issuer authority – and (ii) by sticking with a proper industry protocol for the purpose of exchanging tokens. Some flexibility to define claims that end up in the new token may be lost.

Token Exchange can also be key to avoid sharing tokens across services, while it may require Authorino to keep track (occasionally refreshing) its own tokens on the other hand.

Footnotes

  1. For Security Considerations of the OAuth2 protocol, see Section 10 of RFC 6749.

@denniskniep
Copy link

denniskniep commented Jun 25, 2024

Moved this feature request here: #477,
because its not releated OAuth 2.0 Token Exchange (RFC 8693)

Sorry for confusion

@guicassolato
Copy link
Collaborator Author

Thank you so much, @denniskniep!

I promise to loop back with more detailed comments in a couple of days. Right now, of the top of my head,

  1. I would like to focus on urn:ietf:params:oauth:grant-type:token-exchange grant type and not authorization_code
  2. Client secret spec should probably be based on SecretKeyReference type
  3. Unlike DenyWithSpec which influences the proxy regarding the response to build to the client, WrappedSuccessResponseSpec affects the request that is sent to upstream, so it won't work out of the box for setting cookies. I need to read more about your proposed ErrorDenyWith though I believe it goes along the lines of this other request: Allow generated Festival Wristbands to use wrapper set-cookie #402, and therefore I'd probably separate it into its own change.

@guicassolato guicassolato added this to the v0.18.x milestone Jul 5, 2024
@guicassolato guicassolato modified the milestones: v0.18.x, v0.19.x Aug 21, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: No status
Status: No status
Development

No branches or pull requests

2 participants