Can you validate client key and secret without generating a token?

The scenario as an OAuth2 password grant. We want to validate the client key & secret and assuming that is correct then validate the resource owner user id and password.

Looking through documentation I can't see a way to validate key & secret without generating a token - which would then have to be revoked if resource owner authentication failed.

Solved Solved
3 18 6,895
1 ACCEPTED SOLUTION

adas
New Member

@Dave Pickard while I am still trying to understand the usecase, I believe this is still doable. You can use the verifyapikey policy to validate the client id, the policy also populates the flow variables for client secret, app, developer etc. You can write a separate javascript to compare the client secret sent in the request to the one generated in the flow variable and raise fault if they dont match. Will that work for you ?

View solution in original post

18 REPLIES 18

Hi @Dave Pickard,

I don't think you can validate the client key and secret without going through the OAuthV2 policy and generating a token (if the keys are valid) with the password grant type. If I'm wrong, someone please chime in.

However you can choose not to validate the user credentials until after the key/secret are validated. That's your choice. If the GenerateResponse element of the OAuthV2 policy is set to false (the default), the generated token is not immediately sent back to the client. Rather, it's put into a flow variable. At that point, you can do your user validation and return the token only if that succeeds. You can use AssignMessage policy or a JavaScript policy to get the token from its variable and put it into the response.

If you want to revoke the token you could call the OAuthV2 policy again if the user auth failed, and pass it the token (by setting the <Token> element with name of the variable that contains the token and using the InvalidateToken operation. See this topic on approving/revoking tokens for details. Although, if you never pass the token back, you probably don't need to bother revoking it.

Will

Thanks @wwitman. That's kind of what I was doing at the moment although I may try what @arghya das suggests below.

adas
New Member

@Dave Pickard while I am still trying to understand the usecase, I believe this is still doable. You can use the verifyapikey policy to validate the client id, the policy also populates the flow variables for client secret, app, developer etc. You can write a separate javascript to compare the client secret sent in the request to the one generated in the flow variable and raise fault if they dont match. Will that work for you ?

Thanks @arghya das - that's an interesting way round it. Hadn't really thought about validating secret afterwards - I'll give that a go. Seems a bit strange the VerifyApiKey policy doesn't have the option of using secret as well.

> I believe this is still doable.

It is Absolutely possible!

Don't even need Javascript to do it; could use BasicAuthentication + VerifyApiKey policies and a simple condition on a step.

The BasicAuthentication policy will extract the key + secret from a Besic Auth (base64) header.

VerifyApiKey will check the extracted key and implicitly retrieve the stored secret

then , apply a Condition on a step like so:

<Step>
  <Condition>extracted.secret != provided.secret</Condition>
  <Name>RaiseFault-SecretMismatch</Name>
</Step> 

@Dino

You are absolutely correct. I was suggesting javascript because there you have better control over what sort of comparisons you want to do and what other checks you want to perform. But the point is that, using the verifyApiKey policy you could do the client_id and client_secret validation since its populating the secret as a flow variable on successful execution of the policy.

I've implemented the above. And it works flawlessly. Thank you. I say it's the most straight forward approach to the question posted above compared to others that are using oAuth policies for this.

i've used basic auth to decode the base64 encoded header from the request and populate the client id and client secret fields, used verify api key to validate the client id. And as suggested above i have created a step condition as follows :

 

            <Step>
                <Condition>(decoded.clientSecret != verifyapikey.CS-VerifyClientKey.client_secret)</Condition>
                <Name>RF-Invalid-Client-Secret</Name>
            </Step>

This above condition raises a fault rule policy that gets executed only if the client secret in the header does not match with the one that is stated in the "Apps".

 

Thanks for posting your experience.

It's nice to see that existing answers to old questions still apply!

Just make sure your client (app) is authorized for an API Product, otherwise VerifyAPIKey won't work.

adas
New Member

@Dave Pickard The purpose of verifyApiKey policy is to validate the app is authorized to make api calls to a particular proxy (or api product). So validating the client_secret may not always make sense.

Coming back to my point about the flow variables, here is the list of flow variables that would get populated as part of successful execution of the policy:

verifyapikey.VerifyAPIKey.apiproduct.name     
verifyapikey.VerifyAPIKey.client_id     
verifyapikey.VerifyAPIKey.client_secret     
verifyapikey.VerifyAPIKey.developer.app.id      
verifyapikey.VerifyAPIKey.developer.app.name      
verifyapikey.VerifyAPIKey.developer.email     
verifyapikey.VerifyAPIKey.developer.id      
verifyapikey.VerifyAPIKey.DisplayName     
verifyapikey.VerifyAPIKey.expires_in      
verifyapikey.VerifyAPIKey.failed      
verifyapikey.VerifyAPIKey.issued_at     
verifyapikey.VerifyAPIKey.Notes     
verifyapikey.VerifyAPIKey.redirection_uris      
verifyapikey.VerifyAPIKey.status 

This should work for you. Let me know, if this resolves your query and accept the answer.

Can I use these variables as is or do I need to extract them as mentioned here in the `AccessPolicy Flow Variables` in the Samples section? I tried both (using directly and extracting), but my comparison fails 😞

https://docs.apigee.com/api-services/reference/verify-api-key-policy

I don't know what you mean by "AccessPolicy Flow Variables".

The doc for the VerifyApiKey policy states that the policy will set flow variables (aka context variables) as it executes. Subsequent policies can then read those variables to determine various things. The list of variables is as stated above in the answer by Arghya, and is also listed in the doc page.

There is no need to "extract" anything.

Re: "I tried both and my comparison fails" - You'll have to be more specific about what you mean by "I tried both". Tried means .. what exactly ?

But here's what I suggest: you have a NEW question. Certainly it's related to this question that was asked and answered 2 years ago, but obviously it's a NEW question. So: ask a new question using the "Ask a Question" button.

6360-ask-a-question.png

When you ask the question, provide this information:

  • what does your general policy flow look like ?
  • When do you call "VerifyApiKey" ?
  • Are you certain the API Key is valid for the proxy / product?
  • Where are you using the variables, how are you "using" them, how are you referring to them? in which context?
  • what results are you observing?
  • what results do you expect to observe?
  • how are those two things different?

Thanks @Dino.

What I meant was the tab that is labeled "Access Policy Flow Variables". Worked out for me after I followed the instructions there.

And yes, next time, will ask a new question 🙂

Thanks again!

Not applicable

This is cool @arghya das. I wasn't aware that VerifyAPIKey returns those variables. In the past, I've used Apigee Access Entity Policy along with the Client Id to retrieve the Client Secret. Then, I'm able to use it validate a JWT token.

The use case that I and perhaps more developers see, it is that you can sign JWT with the client secret and then in Apigee validate the signature of the JWT using the same client secret with HMAC algorithm. Since you can embed the client key as part of JWT claims. Or you can even implement private/public key validation by storing the public key as a a custom attribute.

@Dino, @Vinit Mehta - Perhaps you guys want to weigh in on this one.

When validating a JWT signed with a public key, I do it in maybe 3 phases:

  1. Verify the JWT's signature, and times, issuer, audience, etc (should I consider the token to be valid?)
  2. Verify claims on the JWT against required claims
  3. If there's a client_id in there, call VerifyApiKey

If it's a JWT signed with a secret key, then you'd want to do something a little different:

  1. extract the client_id from the unverified JWT body
  2. call VerifyApiKey to verify that the client_id is valid, and implicitly retrieve the secret
  3. verify the JWT signature, times, issuer, audience

@Diego Zuluaga I was recently trying something out and came across this, but like I said earlier the purpose of the verifyApiKey policy is different. But if someone wants to validate the key as well, they can do this approach and save on extra callouts to the datastore to execute policies like access entity or access token.

Excellent @arghya das and @Dino. Great discussion! I'm putting this in a list of projects to implement for a hackday. Especially the public and private using RSA along with VerifyAPIKey Policy for a reference architecture leveraging some of the strategies suggested here.

I understand the use case, you only want valid clients hitting the endpoint before attempting to authenticate the user via a Service Callout.

You can validate the client Id and Secret using an OAuth2 policy with GenerateAccessToken operation configured to not store the token and to not send a response.

So your flow would look like:

6365-screen-shot-2018-01-31-at-83318-am.png

The policy checks for valid client_id and verifies the client_secret, it also requires a username and password to be present. Note the use of StoreToken and GenerateResponse.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<OAuthV2 async="false" continueOnError="false" enabled="true" name="OA-VerifyAPIKey-and-Secret">
    <DisplayName>OA-VerifyAPIKey-and-Secret</DisplayName>
    <ExternalAuthorization>false</ExternalAuthorization>

    <StoreToken>false</StoreToken>
    <GenerateResponse enabled="false"/>
    <GenerateErrorResponse enabled="true"/>

    <Operation>GenerateAccessToken</Operation>
    <ExpiresIn>3600000</ExpiresIn>
    <SupportedGrantTypes>
        <GrantType>password</GrantType>
    </SupportedGrantTypes>
    <ClientId>request.formparam.client_id</ClientId>
    <GrantType>request.formparam.grant_type</GrantType>
    <UserName>request.formparam.username</UserName>
    <PassWord>request.formparam.password</PassWord> 
    <Tokens/>
</OAuthV2>

Here's the request:

6367-screen-shot-2018-01-31-at-91741-am.png