oauth4webapi

repository·main·Indexed 20 days ago

https://github.com/panva/oauth4webapi

A low-level, zero-dependency JavaScript library for implementing OAuth 2.1, OpenID Connect, and FAPI 2.0 client logic across browsers and server-side runtimes. It provides secure implementations for Authorization Code Grant, Client Credentials, CIBA, and Device Authorization flows, supporting security features like PKCE, DPoP, PAR, and various client authentication methods including PrivateKeyJwt and mTLS.

Tokens
62.6K
Snippets
209
Records
305
Agent score
71%

What's inside oauth4webapi

  1. Overview of oauth4webapi

    main

    oauth4webapi

    oauth4webapi is a low-level OAuth 2 and OpenID Connect (OIDC) client API for JavaScript runtimes. It is designed to provide secure, up-to-date implementations of OAuth 2.1, OAuth 2.0 (with latest Security BCP), FAPI 2.0, and OIDC.

    Key characteristics:

    • Security Focused: Promotes secure best practices like PKCE, DPoP, and PAR.
    • Runtime Agnostic: Uses only capabilities common to both browser and non-browser JavaScript runtimes.
    • Zero Dependencies: The package has no external dependencies and exports tree-shakeable ESM.
    • Certified: Conforms to OpenID Connect Basic, FAPI 1.0, and FAPI 2.0 Relying Party Conformance Profiles.
  2. Accessing Protected Resources

    main

    To interact with a protected resource (API) using OAuth 2.0 or OIDC, use the following functions to manage requests and user information:

    • protectedResourceRequest: Construct a request to a protected resource.
    • userInfoRequest: Construct a request to the UserInfo endpoint.
    • processUserInfoResponse: Process the response received from the UserInfo endpoint.
  3. Use the jwksCache option for persistent JWKS storage

    main

    The jwksCache symbol is used to provide a persistent JSON Web Key Set (JWKS) cache in environments like cloud computing runtimes (e.g., AWS Lambda, Google Cloud Functions) where in-memory caching is not preserved between invocations.

    When you pass an object using the [oauth.jwksCache] key into functions that accept JWKSCacheOptions, the module uses that object to:

    1. Serve as the initial JWKS value (avoiding an immediate HTTP request).
    2. Update the object's properties if the module triggers a new HTTP request to fetch the JWKS.

    Security Warning

    This option has security implications. You must ensure that the JWKS cache object is only writable by your own code to prevent unauthorized key injection.

    Implementation Pattern

    To use this correctly in a stateless environment:

    1. Retrieve: Before calling the OAuth function, fetch the previously cached object from a low-latency key-value store (like Redis or a cloud-native KV store). Default to an empty object {} if no cache exists.
    2. Execute: Pass the retrieved object into the function's options using the [oauth.jwksCache] symbol.
    3. Detect Changes: Check if the uat (Update At) property of the object has changed compared to the version you initially retrieved.
    4. Persist: If uat has changed, save the updated object back to your key-value store.
    let as!: oauth.AuthorizationServer
    let request!: Request
    let expectedAudience!: string
    let getPreviouslyCachedJWKS!: () => Promise<oauth.ExportedJWKSCache>
    let storeNewJWKScache!: (cache: oauth.ExportedJWKSCache) => Promise<void>
    
    // 1. Load JSON Web Key Set cache from external storage
    let jwksCache: oauth.JWKSCacheInput = (await getPreviouslyCachedJWKS()) || {}
    let { uat } = jwksCache
    
    // 2. Use JSON Web Key Set cache in an OAuth function
    let accessTokenClaims = await oauth.validateJwtAccessToken(as, request, expectedAudience, {
      [oauth.jwksCache]: jwksCache,
    })
    
    // 3. If the module updated the cache (uat changed), persist it
    if (uat !== jwksCache.uat) {
      await storeNewJWKScache(jwksCache)
    }
  4. Implement Authorization Code Grant

    main

    The Authorization Code Grant flow is implemented using these core functions:

    • authorizationCodeGrantRequest: Initiates the authorization request.
    • processAuthorizationCodeResponse: Processes the response from the authorization endpoint.
    • validateAuthResponse: Validates the authorization response.
    • validateJwtAuthResponse: Validates the authorization response when using JWT-based responses.
    • generateRandomCodeVerifier & calculatePKCECodeChallenge: Used for implementing PKCE (Proof Key for Code Exchange).
    • issueRequestObject: Used for creating signed Request Objects (JAR).
  5. Explore Grant Type examples

    main

    For implementations of specific OAuth 2.0 grant types, refer to these examples:

    • Client Credentials Grant: examples/client_credentials.ts
    • Client-Initiated Backchannel Authentication Grant (CIBA): examples/ciba.ts
    • Device Authorization Grant: examples/device_authorization_grant.ts
    • Refresh Token Grant: examples/refresh_token.ts
  6. Implement Client-Initiated Backchannel Authentication (CIBA)

    main

    For asynchronous authentication flows, use:

    • backchannelAuthenticationRequest: Initiates the CIBA request.
    • processBackchannelAuthenticationResponse: Processes the initial response.
    • backchannelAuthenticationGrantRequest: Requests the token after user approval.
    • processBackchannelAuthenticationGrantResponse: Processes the token response.
  7. Explore Client Authentication examples

    main

    The library supports various methods for client authentication. You can find implementation details for the following in the examples/ directory:

    • Client Secret in HTTP Body: examples/oauth.ts
    • Client Secret in HTTP Authorization Header: examples/client_secret_basic.ts
    • Private Key JWT Client Authentication: examples/private_key_jwt.ts
    • Public Client: examples/public.ts
  8. Implement Device Authorization Grant

    main

    For devices with limited input capabilities (e.g., Smart TVs), use:

    • deviceAuthorizationRequest: Initiates the device flow.
    • deviceCodeGrantRequest: Polls for the token using the device code.
    • processDeviceAuthorizationResponse & processDeviceCodeResponse: Handle the respective responses.
  9. Use Pushed Authorization Requests (PAR)

    main

    To push authorization requests to the server via a backchannel instead of via the browser URL, use:

    • pushedAuthorizationRequest: Sends the request to the PAR endpoint.
    • processPushedAuthorizationResponse: Processes the response from the PAR endpoint.
  10. Explore FAPI (Financial-grade API) examples

    main

    For high-security profiles required in financial applications, use these FAPI implementation examples:

    • FAPI 1.0 Advanced: examples/fapi1-advanced.ts
    • FAPI 2.0 Security Profile: examples/fapi2.ts
    • FAPI 2.0 Message Signing: examples/fapi2-message-signing.ts
  11. Implement Mutual-TLS (mTLS) Client Authentication

    main

    To use Mutual-TLS for client authentication or sender-constrained tokens, set use_mtls_endpoint_aliases: true in your Client metadata. This must be combined with customFetch to provide a Fetch implementation that supports client certificates (e.g., using undici in Node.js or Deno.createHttpClient in Deno).

    import * as undici from 'undici'
    
    let as!: oauth.AuthorizationServer
    let client!: oauth.Client & { use_mtls_endpoint_aliases: true }
    let params!: URLSearchParams
    let key!: string // PEM-encoded key
    let cert!: string // PEM-encoded certificate
    
    let clientAuth = oauth.TlsClientAuth()
    let agent = new undici.Agent({ connect: { key, cert } })
    
    let response = await oauth.pushedAuthorizationRequest(as, client, clientAuth, params, {
      // @ts-ignore
      [oauth.customFetch]: (...args) =>
        undici.fetch(args[0], { ...args[1], dispatcher: agent }),
    })