Built-in basic auth option for proxy, or a work-around

Not applicable

I'm really new to Apigee Edge, and I don't even fully grasp what people use it for. What I can say is that I am using it to set up URLs that are protected by various forms of authentication and can receive arbitrary requests from my app for testing purposes.

I have succeeded in setting up a simple No-Target api proxy, selecting the oauth2 option in the setup wizard for securing it, and following this tutorial carefully: https://docs.apigee.com/api-platform/tutorials/secure-calls-your-api-through-oauth-20-client-credent...

I also see that the setup wizard gives an option to secure an api proxy by requiring an api key, or to simply have no authentication. What I'm still lacking is being able to secure an api proxy with basic auth. It would be nice if there were an option for that right in the setup wizard and if there were a tutorial analogous to that of the oauth2 tutorial. Lacking that, I need to at least figure out a work-around.

Here is what I have been able to figure out so far. When I select an authentication option in the setup wizard for an api proxy, it results in what are called policies being applied to the proxy. These policies are a small set of functions Apigee Edge provides to us which then have configuration options. When I select to use a policy for my proxy, I see xml code which describes the configuration for that policy. Every policy has documentation which then tells me all the ways I can configure it.

I'm not sure how the oauth2 configured proxy makes sure the access token is actually valid, but I was able to figure out how the companion proxy for issuing tokens is able to make sure the client_id and client_secret are valid. I had to create a product, app, and developer that were linked together, and that developer on that app then had its own api key and secret. The proxy then looks up a developer by matching the provided client_id with the dev's stored api key, and then confirms that the provided client_secret matches the dev's stored secret.

I'm guessing my best option for setting up a username/password basic auth system will end up somehow using a dev's api key and secret as if they were a username and password.

Similar to the oauth2 policy's verification operation, I noticed that the Verify API Key policy makes sure the provided api key matches one of the devs on that app and proxy. I also saw that I can configure the policy to look for the api key in whatever part of the request I want. But this policy does not appear to have an option for verifying some provided secret matches the dev's secret. But I saw that I can at least look at the dev's secret from within this policy. Is there a way for this policy to check it against something in the request? Alternatively, is there a way I can add another policy to the chain that can verify these values match?

It looks like I also need to use the Basic Authentication policy, but not for actually authenticating. It looks like it's just for converting the Authorization header into a couple of values for later policies to easily read from. Correct?

Finally, I apologize that this is yet another question in the Community on this topic, but none of the ones I browsed fully answered the question for me. They were either not exactly what I was trying to do or they were written in a way that a newbie like me doesn't understand what's going on.

Solved Solved
0 3 1,525
1 ACCEPTED SOLUTION

Hi @David Ripplinger, Basic authentication policy is simply a policy that can base64 encode or decode a string into its constituent variables. It actually doesn't verify anything like client_id or client_secret or anything like that.

We can make use of Basic Auth, Verify API Key & Raise Fault policy to validate incoming client_id & client_secret.

Steps 1 -

The client would pass the client_id and client_secret as a Base64 encoded in a header. In Basic Auth Policy we can sue a Decode Operation & get client_id in the "decoded.clientId" variable and client_secret in the "decoded.clientSecret" variable.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<BasicAuthentication async="false" continueOnError="false" enabled="true" name="Decoding-Authentication">
    <DisplayName>Decoding-Authentication</DisplayName>
    <Operation>Decode</Operation>
    <IgnoreUnresolvedVariables>false</IgnoreUnresolvedVariables>
    <User ref="decoded.clientId"/>
    <Password ref="decoded.clientSecret"/>
    <AssignTo createNew="false">request.header.Authorization</AssignTo>
    <Source>request.header.Authorization</Source>
</BasicAuthentication>

Step 2 -

Now with Verify API Key policy, we can validate it with a client_id(which refers to the decoded.clientId from Basic Auth policy).

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<VerifyAPIKey async="false" continueOnError="false" enabled="true" name="Verify-Client-Key">
    <DisplayName>Verify-Client-Key</DisplayName>
    <Properties/>
    <APIKey ref="decoded.clientId"/>
</VerifyAPIKey>

If you start the Trace Session, then you will see that Verify API Key also populates the corresponding client_secret as a variable.

Step 3 -

Now after Verify API Key policy we can use a Raise Fault Policy with a Condition to check the client_secret from Trace Variables with decoded.clientSecret & raise a fault if it doesn't match.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<RaiseFault async="false" continueOnError="false" enabled="true" name="Invalid-Client-Secret">
    <DisplayName>Invalid-Client-Secret</DisplayName>
    <Properties/>
    <FaultResponse>
        <Set>
            <Headers/>
            <Payload contentType="application/json">
                \{"error": {"message":"Invalid Client Secret", 
                            "detail":"Provided Client Secret is not valid"}}
            </Payload>
            <StatusCode>401</StatusCode>
            <ReasonPhrase>Invalid Client Secret</ReasonPhrase>
        </Set>
    </FaultResponse>
    <IgnoreUnresolvedVariables>true</IgnoreUnresolvedVariables>
</RaiseFault>

In this way, we can validate both client_id & client_secret.

<Step>
  <Name>Decoding-Authentication</Name>
</Step>
<Step>
  <Name>Verify-Client-Key</Name>
</Step>
<Step>
  <Condition>verifyapikey.Verify-Client-Key.client_secret != decoded.clientSecret</Condition>
  <Name>Invalid-Client-Secret</Name>
</Step>

View solution in original post

3 REPLIES 3

Hi @David Ripplinger, Basic authentication policy is simply a policy that can base64 encode or decode a string into its constituent variables. It actually doesn't verify anything like client_id or client_secret or anything like that.

We can make use of Basic Auth, Verify API Key & Raise Fault policy to validate incoming client_id & client_secret.

Steps 1 -

The client would pass the client_id and client_secret as a Base64 encoded in a header. In Basic Auth Policy we can sue a Decode Operation & get client_id in the "decoded.clientId" variable and client_secret in the "decoded.clientSecret" variable.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<BasicAuthentication async="false" continueOnError="false" enabled="true" name="Decoding-Authentication">
    <DisplayName>Decoding-Authentication</DisplayName>
    <Operation>Decode</Operation>
    <IgnoreUnresolvedVariables>false</IgnoreUnresolvedVariables>
    <User ref="decoded.clientId"/>
    <Password ref="decoded.clientSecret"/>
    <AssignTo createNew="false">request.header.Authorization</AssignTo>
    <Source>request.header.Authorization</Source>
</BasicAuthentication>

Step 2 -

Now with Verify API Key policy, we can validate it with a client_id(which refers to the decoded.clientId from Basic Auth policy).

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<VerifyAPIKey async="false" continueOnError="false" enabled="true" name="Verify-Client-Key">
    <DisplayName>Verify-Client-Key</DisplayName>
    <Properties/>
    <APIKey ref="decoded.clientId"/>
</VerifyAPIKey>

If you start the Trace Session, then you will see that Verify API Key also populates the corresponding client_secret as a variable.

Step 3 -

Now after Verify API Key policy we can use a Raise Fault Policy with a Condition to check the client_secret from Trace Variables with decoded.clientSecret & raise a fault if it doesn't match.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<RaiseFault async="false" continueOnError="false" enabled="true" name="Invalid-Client-Secret">
    <DisplayName>Invalid-Client-Secret</DisplayName>
    <Properties/>
    <FaultResponse>
        <Set>
            <Headers/>
            <Payload contentType="application/json">
                \{"error": {"message":"Invalid Client Secret", 
                            "detail":"Provided Client Secret is not valid"}}
            </Payload>
            <StatusCode>401</StatusCode>
            <ReasonPhrase>Invalid Client Secret</ReasonPhrase>
        </Set>
    </FaultResponse>
    <IgnoreUnresolvedVariables>true</IgnoreUnresolvedVariables>
</RaiseFault>

In this way, we can validate both client_id & client_secret.

<Step>
  <Name>Decoding-Authentication</Name>
</Step>
<Step>
  <Name>Verify-Client-Key</Name>
</Step>
<Step>
  <Condition>verifyapikey.Verify-Client-Key.client_secret != decoded.clientSecret</Condition>
  <Name>Invalid-Client-Secret</Name>
</Step>

Thank you! I was able to figure out eventually before this that the Basic Auth policy was only for coding and decoding, and I was thinking I needed to chain a few policies together, but I wasn't sure how that Raise Fault policy worked nor how to set steps in the request flow with conditions.

For any other newbies that read this, it may not be obvious that creating policies is not the same as using them. After creating them, one has to click on PreFlow or PostFlow and then drag them over. Once dragged over, you'll see steps added to the xml for the flow. That corresponds to this answer's last xml blurb. Dragging them over is not sufficient. Then one needs to write in the condition for the step with the Invalid-Client-Secret policy (a.k.a. the Raise Fault policy).

For a while, I was wondering how to raise faults conditionally. I didn't realize until this answer that conditions are added to the Step xml in a flow.

Side note: I was getting a 500 when no authorization header was present, so I added another step with a Raise Fault policy where the condition was:

<Condition>request.header.authorization = null</Condition>

Then the Raise Fault responded with a 401, which I liked more than a 500.

Again, thank you for the answer! It is all working wonderfully now, and I have a better understanding of how api proxies are configured.

Im glad it worked. I would suggest you go through the eLearning provided by Apigee. These are old training videos(few of them use BaaS which is deprecated now) but the policies & concepts remain the same.

https://academy.apigee.com/courses/elearning

Foundational Training & Edge Developer BootCamp