Skip to content

Enhancing specification to describe token presentation mechanisms for OAuth 2.0 #3612

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

Open
alex-feel opened this issue Feb 28, 2024 Discussed in #2867 · 4 comments
Open
Labels
security: auth Authentication including overlap with authorization security
Milestone

Comments

@alex-feel
Copy link

Issue Description

During a recent discussion in #2867, it became apparent that the OpenAPI Specification lacks explicit guidelines on how clients should present access tokens to resource servers, especially considering the OAuth 2.0 authorization framework. This issue was highlighted in the context of OAuth 2.0's defined grant flows and the need for specifying how access tokens, once obtained, are used for resource access.

Relevant Discussion Points

  • OAuth 2.0 Grant Flows: The specification allows defining OAuth 2.0 flows, like the implicit flow, requiring the authorizationUrl. However, it stops short of detailing how the obtained access tokens should be presented to the resource server.

  • Access Token Presentation: The OAuth 2.0 specification mentions the use of the HTTP "Authorization" request header field with an authentication scheme for the access token type. Yet, "typically" does not encompass all possible scenarios, like the requirement for the Demonstration of Proof-of-Possession at the Application Layer (DPoP), which requires additional headers.

  • OpenAPI Specification Limitation: The current OpenAPI Specification does not support specifying the method of presenting an access token to a resource server. This limitation was acknowledged in a community call discussion.

Proposal for Enhancement

Given the diversity in access token types and presentation methods (e.g., Bearer tokens, DPoP), there is a clear need for the OpenAPI Specification to allow documenting the exact mechanism of access token presentation for a secured endpoint.

Suggested Improvements:

  1. Extend the securitySchemes Object: Introduce new fields within the securitySchemes object to specify the required headers or methods for presenting access tokens, including non-standard approaches.

  2. Community Engagement: Invite contributions and discussions from the OpenAPI community, through TDC calls, workflows sig, or security sig, to refine and agree upon the proposed enhancements.

Conclusion

Enhancing the OpenAPI Specification to include explicit guidelines on access token presentation mechanisms would greatly aid in the accurate and comprehensive documentation of APIs, fostering better interoperability and understanding of secured API endpoints. I look forward to contributing to this discussion and helping drive the necessary changes.

@alex-feel
Copy link
Author

@LasneF @lornajane Based on feedback and aiming for flexibility in specifying authentication schemes, I propose the following adjustment to the scheme attribute in the OpenAPI Specification for 3.2 target branch:

<a name="securitySchemeScheme"></a>scheme | `string` | `http`, `oauth2` | **REQUIRED** for `http`, **OPTIONAL** for `oauth2`. The name of the HTTP Authorization scheme to be used in the [Authorization header as defined in RFC7235](https://tools.ietf.org/html/rfc7235#section-5.1).  The values used SHOULD be registered in the [IANA Authentication Scheme registry](https://www.iana.org/assignments/http-authschemes/http-authschemes.xhtml).

This revision aims to maintain the required status for http scheme while introducing flexibility for oauth2.

Would this approach be preferable, or should we consider adding a new optional field specifically for oauth2 to avoid ambiguity?

I'm looking forward to the community's input to refine this proposal further. If this approach meets the community's approval, I will proceed to create a new PR targeting the 3.2 branch to incorporate these changes.

@ralfhandl ralfhandl added this to the v3.2.0 milestone Aug 16, 2024
@handrews handrews modified the milestones: v3.2.0, v3.3.0 Nov 21, 2024
@handrews handrews modified the milestones: v3.3.0, v3.2.0 Jan 10, 2025
@lornajane
Copy link
Contributor

I'm sorry it's been a while since we discussed this, but I'd like to pick it up again as we prepare for 3.2. I am of the opinion that a field can only be required or optional, so we'd have to have it required in both situations, or choose a different option. @alex-feel can you suggest some alternatives that would make sense for the expected use cases?

@alex-feel
Copy link
Author

@lornajane, thank you for getting back to the discussion and for your feedback on this issue.

My apologies for the delayed response — I was offline for the past day. I understand your point that a field can only be required or optional, and I will explore alternative approaches that might address the expected use cases.

I'll update this thread as soon as I have a potential solution to share.

@lornajane lornajane modified the milestones: v3.2.0, v3.3.0 May 15, 2025
@alex-feel
Copy link
Author

@lornajane,

I’d like to propose a new optional object. This proposal aims to:

  • Cover all RFC 6750 Bearer delivery modes.
  • Stay future-proof for DPoP and other PoP schemes.
  • Reuse naming already familiar in OAS 3.0/3.1, where appropriate.
  • Provide a structured way to describe how access tokens, refresh tokens, and proof-of-possession tokens are presented.

Proposal: tokenPresentation (optional, type: oauth2 only)

This new object would be added to the Security Scheme Object when type is oauth2.

# In SecuritySchemeObject, when type: oauth2
# properties:
#   ... existing keys (type, description, name, in, scheme, bearerFormat, flows, openIdConnectUrl)
tokenPresentation:
  type: object
  description: >
    How a client presents the OAuth 2.0 tokens (access, refresh, proof-of-possession)
    to the resource server or authorization server.
  properties:
    accessToken:
      type: object
      description: >
        Describes how the OAuth 2.0 access token is presented.
        The client MUST use one of the specified mechanisms.
      oneOf:
        - title: Via HTTP Authentication Scheme
          description: >
            The token is presented as part of a standard HTTP Authentication Scheme,
            typically in the 'Authorization' header (e.g., 'Authorization: Bearer <token>').
          required:
            - scheme
          properties:
            scheme:
              type: string
              description: >
                The HTTP Authentication Scheme (e.g., 'bearer', 'dpop')
                registered with IANA. This implies the token is formatted and
                placed according to the rules of that scheme.
                Values should be lowercase.
              example: bearer
            # bearerFormat: # Optional, can be added if scheme is 'bearer'
            #   type: string
            #   description: A hint to the client about the format of the bearer token provided.
            #   example: jwt
        - title: Via Specific Location
          description: >
            The token is presented as a value in a specific header, query parameter,
            or form body parameter. Use this for RFC 6750 query/body methods
            for Bearer tokens, or for custom token transmission methods not
            defined by an IANA-registered HTTP Authentication Scheme.
          required:
            - in
            - name
          properties:
            in:
              type: string
              enum: [header, query, body]
              description: >
                Location of the access token.
                For `body`, this implies an `application/x-www-form-urlencoded`
                request body (RFC 6750 §2.2).
                Note (RFC 6819): Transmitting tokens in URI query parameters is
                strongly discouraged due to security risks.
            name:
              type: string
              description: >
                The name of the header, query, or body parameter carrying the token.
                Header names should be treated case-insensitively by implementations,
                but specified here in their canonical form.
                Values should be lowercase.
              example: access_token # Or a custom header name like 'X-API-Token'
    refreshToken:
      type: object
      description: >
        Describes how the OAuth 2.0 refresh token is presented, typically
        to the token endpoint of an authorization server.
      required:
        - in
        - name
      properties:
        in:
          type: string
          enum: [header, query, body] # Query is highly discouraged for refresh tokens.
          default: body
          description: >
            Location of the refresh token. Typically 'body' with
            `application/x-www-form-urlencoded`.
        name:
          type: string
          default: refresh_token
          description: >
            The name of the body parameter or header carrying the refresh token.
            Values should be lowercase.
          example: refresh_token
    proofTokens:
      type: array
      description: >
        An array describing additional HTTP elements (headers, parameters)
        required by proof-of-possession schemes. Each item in the array
        defines one such element.
      items:
        type: object
        description: Describes a single proof-of-possession element.
        oneOf:
          - title: Via Proof Scheme
            description: >
              The proof element is defined by a known scheme that dictates its
              name and location (e.g., the 'dpop' scheme implies the 'DPoP' header).
            required:
              - scheme
            properties:
              scheme:
                type: string
                description: >
                  The scheme defining the proof element (e.g., 'dpop' for the DPoP header).
                  This implies the standard location and name for that proof scheme.
                  Values should be lowercase.
                example: dpop
          - title: Via Specific Location for Proof
            description: >
              The proof element is explicitly placed in a header, query parameter, or body.
              Use for multi-part PoP mechanisms (e.g., HTTP Message Signatures)
              or custom proof elements.
            required:
              - in
              - name
            properties:
              in:
                type: string
                enum: [header, query, body]
                default: header
                description: Location of this specific proof element.
              name:
                type: string
                description: >
                  Name of the header or parameter for this proof element.
                  Header names should be treated case-insensitively.
                  Values should be lowercase.
                example: signature-input # Another example: 'signature'
  # No top-level 'required' field for tokenPresentation itself.

Usage examples

1. Bearer token via Authorization header (RFC 6750 §2.1)

components:
  securitySchemes:
    oauth2AuthCodeBearerHeader:
      type: oauth2
      flows:
        authorizationCode: # ... full flow definition ...
          tokenUrl: https://example.com/oauth/token
          authorizationUrl: https://example.com/oauth/authorize
          scopes:
            read:pets: read pets
      tokenPresentation:
        accessToken:
          scheme: bearer # Implies Authorization: Bearer <token>

2. Bearer token via form-encoded body (RFC 6750 §2.2)

components:
  securitySchemes:
    oauth2BearerBody:
      type: oauth2
      flows:
        clientCredentials: # ... full flow definition ...
          tokenUrl: https://example.com/oauth/token
          scopes:
            read:pets: read pets
      tokenPresentation:
        accessToken:
          # NOT scheme: bearer here
          in: body
          name: access_token # The token value itself is a Bearer token

3. Bearer token via URI query (RFC 6750 §2.3)

components:
  securitySchemes:
    oauth2BearerQuery:
      type: oauth2
      flows:
        implicit: # ... full flow definition ...
          authorizationUrl: https://example.com/oauth/authorize
          scopes:
            read:pets: read pets
      tokenPresentation:
        accessToken:
          # NOT scheme: bearer here
          in: query
          name: access_token # The token value itself is a Bearer token

4. DPoP-bound access token (RFC 9449)

components:
  securitySchemes:
    oauth2DPoP:
      type: oauth2
      flows:
        authorizationCode: # ... full flow definition ...
          tokenUrl: https://example.com/oauth/token
          authorizationUrl: https://example.com/oauth/authorize
          scopes:
            api:read: read api resources
      tokenPresentation:
        accessToken:
          scheme: dpop # Implies Authorization: DPoP <access_token>
        proofTokens:
          - in: header # This example explicitly defines the DPoP header's location and name.
            name: dpop   # Alternatively, { scheme: dpop } could be used here.

5. Refresh token presentation

components:
  securitySchemes:
    oauth2WithRefresh:
      type: oauth2
      flows:
        authorizationCode: # ...
          tokenUrl: https://example.com/oauth/token
          authorizationUrl: https://example.com/oauth/authorize
          refreshUrl: https://example.com/oauth/refresh
          scopes:
            offline_access: allow refresh token
      tokenPresentation:
        accessToken:
          scheme: bearer # Example: access token uses standard Bearer header
        refreshToken:
          in: body # Default
          name: refresh_token # Default

Why this shape?

Requirement How it’s satisfied
All RFC 6750 Bearer delivery modes The accessToken oneOf construct allows specifying:
1. scheme: bearer for Authorization: Bearer <token>.
2. in: query, name: access_token for query parameters.
3. in: body, name: access_token for form body parameters.
Support for Refresh Tokens Dedicated refreshToken object with in and name allows clear description of its presentation.
Future-proof PoP schemes proofTokens as an array of objects (each defined either by scheme or by in and name) allows describing multiple, varied PoP elements.
Clear structure for Access Token accessToken.oneOf clearly distinguishes between using a formal HTTP Authentication Scheme and placing a token directly in a specific location.
Consistent Naming (where applicable) Uses scheme for the HTTP Authentication Scheme concept. Uses in and name consistent with parameter descriptions.
No duplication with type: http, scheme: dpop tokenPresentation is specific to type: oauth2 and describes how tokens obtained via OAuth2 flows are used. type: http with scheme: dpop can still be used for non-OAuth2 scenarios or simpler DPoP descriptions if preferred.
JSON Schema friendly (with oneOf) While oneOf adds some complexity, it accurately models the "either/or" requirement for accessToken presentation, providing a choice between definition via scheme or via explicit in and name.
Backward compatible As a new optional field within type: oauth2 security schemes, existing OpenAPI documents that omit tokenPresentation remain valid.

Open questions for the SIG

  1. Given the oneOf structure for accessToken, are the descriptions for each branch clear enough to guide usage (i.e., when to use scheme vs. in/name)? Or is it enough to have just in and name?
  2. Should cookie be added as an option to tokenPresentation.accessToken.oneOf.properties.in (the "Specific Location" branch) to describe access tokens passed via cookies? If so, what should name typically be for in: cookie?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
security: auth Authentication including overlap with authorization security
Projects
None yet
Development

No branches or pull requests

4 participants