Skip to content

Enhanced TokenReview identity object #394

Closed
@guicassolato

Description

When Authorino performs a TokenReview, the structure of the extracted identity object varies depending whether the token is a JWT (e.g. a Kubernetes SA token) or an opaque token (e.g. OpenShift OAuth token). While for JWTs, the identity object is the JWT payload, for opaque tokens, it is the status.user from the TokenReview response.

This behaviour makes it hard to write authorisation rules based on attributes of the identity object, especially in hybrid authentication systems where both kinds of tokens are simultaneously accepted. Differences such as the name of the property that carries the subject ID (i.e., either sub or username) and presence/absence of relevant properties (e.g. iss, groups) can be hard to workaround, often requiring extra requests to the cluster authentication system to retrieve further user info – when such endpoint is available! –, and/or repetition of configuration under when conditions (only to control the expected identity object format).

Authorino extended properties feature doesn't play nice to normalise tokens in such cases where the source value of new (extension) property may not be present.

To exemplify the issue...

The payload of a Kubernetes JWT issued for a SA looks like the following:

{
  "aud": [
    "https://kubernetes.default.svc"
  ],
  "exp": 1684835538,
  "iat": 1684834938,
  "iss": "https://kubernetes.default.svc",
  "kubernetes.io": {
    "namespace": "talker-api",
    "serviceaccount": {
      "name": "talker-api-bff",
      "uid": "bd2d95ad-5ca8-42fc-a6f8-e11600094b44"
    }
  },
  "nbf": 1684834938,
  "sub": "system:serviceaccount:talker-api:talker-api-bff"
}

The status subresource returned with the response to the TokenRequest request in an OpenShift cluster looks like the following:

{
  "audiences": [
    "https://kubernetes.default.svc"
  ],
  "authenticated": true,
  "user": {
    "groups": [
      "system:serviceaccounts",
      "system:serviceaccounts:talker-api",
      "system:authenticated"
    ],
    "uid": "bd2d95ad-5ca8-42fc-a6f8-e11600094b44",
    "username": "system:serviceaccount:talker-api:talker-api-bff"
  }
}
  • While JWT claims such as iss (from the SA JWT) as well as user info such as user.groups (from the TokenReview status) can both be useful for writing authorisation rules; nevertheless either only one or the other may be present.
  • Writing an authorisation check (e.g. SubjectAccessReview) with user ID fetched from either auth.identity.sub or auth.identity.username is not straightforward.

The user should have a way to tell if the JWT payload or the returned user info is desired to fill the identity object. By default, I'd personally go with the user info, but only if the keeping of other attributes, such as audiences, was possible (not the case today).

A few alternatives considered:

  1. Both objects (JWT and TokenRequest status) could be merged together when the token is a JWT, such as in a structure like the following:
    type KubeTokenIdentity struct {
      Token struct {
        Opaque string `json:"opaque,omitempty"`
        JWT    any    `json:"jwt,omitempty"`
      } `json:"token"`
    
      Status kubeauthnv1.TokenReviewStatus `json:"status"` // https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#tokenreviewstatus-v1-authentication-k8s-io
    }
  2. The API for the TokenReview identity verification method in the AuthConfig could include a field to use the JWT payload:
    identity:
    - name: k8s-tokens
      kubernetes:
        jwt: true # default: false
  3. Possibility to extend the identity object conditionally, e.g. by setting a (default) value only in case the property is absent in the source object – only for the issue of sub vs username:
    identity:
    - name: k8s-tokens
      kubernetes: {}
      extendedProperties:
      - name: username
        valueFrom:
          authJSON: auth.identity.sub.@default:{"value":auth.identity.username}

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions