[
https://issues.apache.org/jira/browse/NIFI-14380?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
]
David Handermann updated NIFI-14380:
------------------------------------
Fix Version/s: 2.4.0
Resolution: Fixed
Status: Resolved (was: Patch Available)
> OAuth 2.0 Controller Service to support JWT Bearer Flow
> -------------------------------------------------------
>
> Key: NIFI-14380
> URL: https://issues.apache.org/jira/browse/NIFI-14380
> Project: Apache NiFi
> Issue Type: New Feature
> Components: Extensions
> Reporter: Pierre Villard
> Assignee: Pierre Villard
> Priority: Major
> Fix For: 2.4.0
>
> Time Spent: 3h 40m
> Remaining Estimate: 0h
>
> The goal of this JIRA is to have a new implementation of the
> OAuth2AccessTokenProvider in order to support the JWT Bearer Flow.
> Looking at different services supporting this option, it looks like that each
> implementation have some nuances that will require some handling in order to
> provide a generic solution that works in all of those situations.
> Services that I took into consideration for the implementation:
> h2. Google Identity
> [https://developers.google.com/identity/protocols/oauth2/service-account#httprest]
> The JWT header should include:
> * alg = RS256
> * typ = JWT
> * kid (optional for the key ID)
> Required JWT claims:
> * iss (issuer)
> * scope
> * aud (audience)
> * exp (expiration)
> * iat (issued at)
> Additional optional JWT claim: sub
> The access token request should be application/x-www-form-urlencoded and have
> the following parameters:
> * grant_type = urn:ietf:params:oauth:grant-type:jwt-bearer
> * assertion = signed JWT
> h2. Salesforce
> [https://help.salesforce.com/s/articleView?id=xcloud.remoteaccess_oauth_jwt_flow.htm&type=5]
> The JWT header should include:
> * alg = RS256
> Required JWT claims:
> * iss (issuer)
> * aud (audience)
> * sub (subject)
> * exp (expiration)
> The access token request should be application/x-www-form-urlencoded and have
> the following parameters:
> * grant_type = urn:ietf:params:oauth:grant-type:jwt-bearer
> * assertion = signed JWT
> * format (optional to specify the format of the response, JSON is default)
> Note that the access token returned by Salesforce does NOT include an
> expiration information.
> h2. Microsoft
> [https://learn.microsoft.com/en-us/entra/identity-platform/v2-oauth2-client-creds-grant-flow#second-case-access-token-request-with-a-certificate]
> The JWT header should include:
> * alg = PS256
> * typ = JWT
> * x5t = thumbprint of the certificate
> Required JWT claims:
> * iss (issuer)
> * aud (audience)
> * sub (subject)
> * exp (expiration)
> * jti (JWT ID)
> * nbf (not before)
> * iat (issued at)
> The access token request should be application/x-www-form-urlencoded and have
> the following parameters:
> * grant_type = client_credentials
> * tenant
> * client_id
> * scope
> * client_assertion_type =
> urn:ietf:params:oauth:client-assertion-type:jwt-bearer
> * client_assertion = signed JWT
> * format (optional to specify the format of the response, JSON is default)
> h2. Box
> [https://developer.box.com/guides/authentication/jwt/without-sdk/#3-create-jwt-assertion]
> The JWT header should include:
> * alg = RS256, RS384, or RS512
> * typ = JWT
> * kid = key ID
> Required JWT claims:
> * iss (issuer)
> * aud (audience)
> * sub (subject)
> * exp (expiration)
> * jti (JWT ID)
> * nbf (not before)
> * iat (issued at)
> * box_sub_type - custom claim for Box
> The access token request should be application/x-www-form-urlencoded and have
> the following parameters:
> * grant_type = client_credentials
> * client_id
> * client_secret
> * assertion = signed JWT
> —
> For additional information:
> [https://cloudentity.com/developers/basics/oauth-grant-types/using-jwt-profile-for-authorization-flows/]
> [https://cloudentity.com/developers/api/authorization_apis/oauth2/#tag/oauth2/operation/token]
> —
> h2. Requirements
> Given the above examples, here are some requirements for the implementation:
> * there are many potential fields in the JWT header and the initial
> implementation will not support all fields. Focus will be on supporting alg,
> typ, kid and x5t. The latest requires a certificate to be provided in
> addition to the private key for signing the JWT. Given that x5t does not seem
> to be commonly required, the implementation will require a KeyPairService to
> be specified to provide the private key and if x5t is needed in the JWT
> header, then a SSL Context Service will also be required to provide the
> certificate.
> * some custom claims may be needed so support for dynamic properties will be
> required so that a user can provide custom claims in addition to the common
> ones that will be exposed in the configuration via dedicated properties.
> Dynamic properties where the key will be prefixed by CLAIM. will be used for
> custom claims.
> * the request made against the token endpoint may have very different form
> parameters so support for custom params is also required. Dynamic properties
> where the key will be prefixed by FORM. will be used for custom form
> parameters. Besides we have seen that some sensitive values may be set there
> (client_secret) so support for sensitive dynamic properties is required.
> Finally, the parameter for the signed JWT is not always named "assertion" so
> specific handling is required for this.
> * the returned access token may not have an expiration date so we need
> special handling for re-acquiring a new access token. The following strategy
> is used:
> ** expose an expiration time property for the JWT that defaults to 1 hour
> ** if the retrieved access token has an expiration date, use this + refresh
> window to acquire a new access token
> ** if the retrieved access token does not have an expiration date, use the
> expiration time of the JWT + refresh window to acquire a new access token
> * Signing algorithms supported should also support more robust options even
> if above listed services are not always expecting the most secure ones. The
> following list of signing algorithms should be supported
> ** RS256, RS384, RS512
> ** PS256, PS384, PS512
> ** ES256, ES384, ES512
> ** Ed25519
> * Special validation will be required to make sure that the algorithm of the
> private key provided by the user is compatible with the signing algorithm.
> RS/PS should be with RSA Private Key, ES should be with EC Private Key, etc.
> h2. Configuration
> Following configuration is considered for the implementation:
> * Token Endpoint - URL to request to get the access token
> * Web Client Service to perform the request
> * Private Key Service to provide the private key for signing the JWT
> * Signing Algorithm with allowable values mentioned before
> * Issuer - optional
> * Subject - optional
> * Audience - optional - space separated values if multiples should be
> specified
> * Scope - optional
> * NBF - true/false to include the not before time that will be set to same
> value as issue time
> * JTI - optional - recommendation to use expression language ${UUID()} is set
> * Header Type - true/false to include or not typ=JWT in the header
> * Header X5T - true/false to include or not the x5t field with the
> thumbprint of the certificate. If set to true, then
> ** required SSL Controller Service to provide the certificate to use
> * KID - optional
> * Grant Type - value to use for the grant_type form parameter, defaults to
> urn:ietf:params:oauth:grant-type:jwt-bearer
> * Assertion Field - name of form parameter that should contain the signed
> JWT (defaults to assertion but can be overridden to account for the Microsoft
> case)
> * Refresh window - defaults to 5 minutes
> * JWT validity - defaults to 1 hour
> In addition, support for sensitive dynamic properties with
> * CLAIM.<key> = <value> - to set custom claims
> * FORM.<key> = <value> - to set custom form parameters in the request
> h2. Conclusion
> With the proposed configuration options above, here is how the controller
> service would be configured for the different services
> h3. Google Identity
> * Token Endpoint - [https://oauth2.googleapis.com/token]
> * Signing Algorithm - RS256
> * Issuer - The email address of the service account
> * Subject - optional - The email address of the user for which the
> application is requesting delegated access
> * Audience - [https://oauth2.googleapis.com/token]
> * Scope - A space-delimited list of the permissions that the application
> requests
> * NBF - false
> * JTI - not set
> * Header Type - true
> * Header X5T - false
> * KID - key ID of the service account key
> * Grant Type - urn:ietf:params:oauth:grant-type:jwt-bearer
> * Assertion Field - assertion
> h3. Salesforce
> * Token Endpoint - [https://.../services/oauth2/token]
> * Signing Algorithm - RS256
> * Issuer - The issuer must contain the OAuth client_id or the connected app
> for which you registered the certificate
> * Subject - optional - If you’re implementing this flow for an Experience
> Cloud site, the subject must contain the user’s username.
> * Audience - The audience identifies the authorization server as an intended
> audience.
> * Scope - not set
> * NBF - false
> * JTI - not set
> * Header Type - false
> * Header X5T - false
> * KID - not set
> * Grant Type - urn:ietf:params:oauth:grant-type:jwt-bearer
> * Assertion Field - assertion
> h3. Box
> * Token Endpoint - https://api.box.com/oauth2/token
> * Signing Algorithm - RS256, RS384, or RS512
> * Issuer - The Box Application's OAuth client ID
> * Subject - The Box Enterprise ID if this app is to act on behalf of the
> Service Account of that application, or the User ID if this app wants to act
> on behalf of another user.
> * Audience - https://api.box.com/oauth2/token
> * Scope - not set
> * NBF - true
> * JTI - ${UUID()}
> * Header Type - true
> * Header X5T - false
> * KID - The ID of the public key used to sign the JWT. Not required, though
> essential when multiple key pairs are defined for an application.
> * Grant Type - urn:ietf:params:oauth:grant-type:jwt-bearer
> * Assertion Field - assertion
> * CLAIM.box_sub_type - enterprise or user depending on the type of token
> being requested in the sub claim
> * FORM.client_id - Client ID
> * FORM.client_secret - Client Secret
> h3. Microsoft
> * Token Endpoint - https://.../<tenant>/oauth2/v2.0/token
> * Signing Algorithm - PS256
> * Issuer - Use the GUID application ID.
> * Subject - Use the same value as issuer.
> * Audience - https://login.microsoftonline.com/<tenantId>/oauth2/v2.0/token
> * Scope - not set
> * NBF - true
> * JTI - ${UUID()}
> * Header Type - true
> * Header X5T - true + SSL Controller Service to provide certificate
> * KID - not set
> * Grant Type - client_credentials
> * Assertion Field - client_assertion
> * FORM.client_id - The application ID that's assigned to your app.
> * FORM.tenant - The directory tenant the application plans to operate
> against, in GUID or domain-name format.
> * FORM.scope - The value passed for the scope parameter in this request
> should be the resource identifier (application ID URI) of the resource you
> want, suffixed with .default. All scopes included must be for a single
> resource. Including scopes for multiple resources will result in an error.
> For the Microsoft Graph example, the value is
> https://graph.microsoft.com/.default.
> * FORM.client_assertion_type -
> urn:ietf:params:oauth:client-assertion-type:jwt-bearer
--
This message was sent by Atlassian Jira
(v8.20.10#820010)