[ 
https://issues.apache.org/jira/browse/NIFI-14380?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

Pierre Villard updated NIFI-14380:
----------------------------------
    Status: Patch Available  (was: In Progress)

> 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
>
> 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)

Reply via email to