Vijay Jammi created NIFI-8413:
---------------------------------

             Summary: Oidc Identity Provider should support assertions as 
client credentials for authenticating against the token endpoint      
                 Key: NIFI-8413
                 URL: https://issues.apache.org/jira/browse/NIFI-8413
             Project: Apache NiFi
          Issue Type: Improvement
          Components: Security
    Affects Versions: 1.11.4
            Reporter: Vijay Jammi


The current oidc client authentication methods (client_secret_post, 
client_secret_basic) require client credentials (client_secret) to be stored as 
plain text on the client's filesystem, which could also be inadvertently 
checked into source control system.

Due to these and other security considerations, we should be able to use 
assertions as client credentials for authenticating against the token endpoint. 

While using assertions an oidc client will include client_assertion and 
client_assertion_type parameters instead of passing the client_secret for 
authentication.

Details on the OAuth 2.0 specifications for client authentication using jwt and 
assertions can be found under
 # [RFC 7523, Section 2.2 (Using JWTs for Client 
Authentication)|https://tools.ietf.org/html/rfc7523#section-2.2] 
 # [RFC 7521 (Using Assertions for Client 
Authentication)|https://tools.ietf.org/html/rfc7521#section-4.2].

Summary of the changes needed for assertion based client authentication.
 * Generate a pair of private and public key which can be made available to 
Nifi via a keystore on the filesystem. 
 * Make the public key or cert available to the Authorization Server.     
Alternatively to allow key rotations, we could configure a JWKS URL within Nifi 
to allow the authorization server download the public key using the keyId (kid).
 * While building the token request against the token endpoint, need 
StandardOidcIdentityProvider to provide a way to build a private_key_jwt based 
client authentication in addition to the existing client_secret_basic, 
client_secret_post. 
{code:java}
if 
(ClientAuthenticationMethod.PRIVATE_KEY_JWT.equals(clientAuthenticationMethod)){
    
     clientAuthentication = new PrivateKeyJWT(clientId, tokenEndpoint, 
jwsAlgorithm, privateKey, keyId, null);
}else if 
(ClientAuthenticationMethod.CLIENT_SECRET_POST.equals(clientAuthenticationMethod))
 { 
     clientAuthentication = new ClientSecretPost(clientId, clientSecret);
} else { 
     clientAuthentication = new ClientSecretBasic(clientId, clientSecret);
}{code}

 * Encode and sign the JWT token with the private key, conforming to RFC 7523, 
section 2.2 and pass the signed token within the client_assertion request 
parameter along with the client_assertion_type. 
{code:java}
POST https://<auth-host>:<port>/.../token HTTP/1.1
Content-Type: application/x-www-form-urlencoded
client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer&
client_assertion=eyJraWQiOiJkMlY2LS1OUG....hTMfA&
grant_type=authorization_code&
code=9ba8d85b-...-c7a6fb6dae55
{code}

 * The Authorization Server will use the client's public key or cert to verify 
the signature of the JWT and issues the token

 

Nifi Token Exchange Process

When authentication within Nifi is enabled via OpenId Connect, a user is 
re-directed to the configured authorization endpoint (authorization server) via 
Nifi's oidc/request endpoint. The user upon successfully authenticating with 
the authorization server is directed back to Nifi's (oidc/callback endpoint) 
with a temporary authorization code. This authorization code is then exchanged 
for an ID Token by the OidcService via the Oidc Identity Provider 
(StandardOidcIdentityProvider - exchangeAuthorizationCode(authorizationGrant)). 
The identity provider requests this exchange by authenticating itself against 
the token endpoint of the authorization server and presenting the authorization 
grant. The authorization server authenticates the client, validates the 
authorization grant and upon successful validation it issues an access token to 
Nifi.

Currently, the exchangeAuthorizationCode(...) on StandardOidcIdentityProvider 
provides two methods of Client Authentication (client_secret_basic, 
client_secret_post) which are both based on the Authorization Server issuing a 
set of client credentials (client_secret) which are presented to the 
authorization server as client credentials during token exchange. 
{noformat}
public String exchangeAuthorizationCode(final AuthorizationGrant 
authorizationGrant) throws IOException { 
...

// 1 - Build the client authentication using the clientId and clientSecret 
obtained during the registration of the client with the authorization server
if 
(oidcProviderMetadata.getTokenEndpointAuthMethods().contains(ClientAuthenticationMethod.CLIENT_SECRET_POST))
 { 
clientAuthentication = new ClientSecretPost(clientId, clientSecret); 
} else {
 clientAuthentication = new ClientSecretBasic(clientId, clientSecret); 
}

// 2 - Build the token endpoint request with the above client authentication 
TokenRequest request = new TokenRequest(tokenEndpoint, clientAuthentication, 
authorizationGrant); 
HTTPRequest tokenHttpRequest = request.toHTTPRequest();
...

// 3 - Submit the token request and Get the token response TokenResponse 
response = OIDCTokenResponseParser.parse(tokenHttpRequest.send());

// 4 - Upon Successful Response get the ID Token and the User Identity 
if (response.indicatesSuccess()) { 
final OIDCTokenResponse oidcTokenResponse = (OIDCTokenResponse) response; 
final OIDCTokens oidcTokens = oidcTokenResponse.getOIDCTokens();  

// validate the access token and parse the claims  
final IDTokenClaimsSet claimsSet = tokenValidator.validate(oidcJwt, null);  

// attempt to extract the configured claim to access the user's identity; 
default is 'email' 
String identity = 
claimsSet.getStringClaim(properties.getOidcClaimIdentifyingUser()); ... 
}

// 5 - If User Identity could not extracted out of the IDToken, lookup the 
identity against the UserInfo endpoint 
if (StringUtils.isBlank(identity)) { 
... 
final BearerAccessToken bearerAccessToken = oidcTokens.getBearerAccessToken(); 
... 
// 7 - Invoke the UserInfo endpoint to lookup user identity against the 
UserInfo endpoint 
identity = lookupIdentityInUserInfoModified(bearerAccessToken);  ...
} 
... 

// 8 - Convert into a Nifi jwt for retrieval later 
final LoginAuthenticationToken loginToken = new 
LoginAuthenticationToken(identity, identity, expiresIn, 
claimsSet.getIssuer().getValue()); 
return jwtService.generateSignedToken(loginToken);
}{noformat}
 



--
This message was sent by Atlassian Jira
(v8.3.4#803005)

Reply via email to