Search code examples
azureazure-web-app-serviceopenid-connecteasy-auth

App Services EasyAuth for protecting API with OIDC


I currently have a solution where I have an OIDC provider, a client that obtained a token from that provider, and a protected resource (API).

Currently I'm using Easy Auth on the API, so far it seems to validate the token correctly. The only problem is the API needs a secret (not optional).

In my experience, the API should be able to check the signature, audience and expiration etc without needing a client_id and client_password itself. It seems to me that Easy Auth assumes that everything is a client.

Is there a way to secure an API with Easy Auth OIDC, where the API does not require a secret but will just validate the token knowing the IDP? (Or do I need to use another Azure product such as API Management)

enter image description here

Edit: I think I just learned that in this situation I can add any bogus value to secret and it is not used. The Client ID is used as Audience for validation by Easy Auth.

  • Client ID is used as Audience for token validation by Easy Auth.
  • Client secret is ignored completely by Easy Auth. Can be any value.

Solution

  • Sounds like you are following standard behaviour of validating JWTs in the API, rather than using OpenID Connect - so all good. OIDC is mostly a client concern which led to some confusion on my part, and hence my original answer. I thought I'd add a couple of follow on points below, since they may be useful to you in future when working with Azure AD.

    DISTINCT ACCESS TOKENS PER API

    APIs registered in Azure AD APIs follow the rationale behind the Resource Indicators draft spec. Each access token can only be used by a single API. Therefore each API requires a Client ID to be registered, that is written to access tokens issued for that API.

    OAUTH STANDARDS AND MULTIPLE APIs

    When there are multiple APIs, the most standard OAuth 2.0 architecture in a same company setup is for a set of related APIs to forward access tokens to each other. Each API then validates the access token, which is a fast operation. This is very much recommended, and is sometimes called a zero trust architecture. In a microservices architecture this also allows user context to be maintained securely.

    For this to work, a set of related APIs should be able to all declare the same audience, such as api.mycompany.com. Meanwhile each API will have different scopes, to ensure that if an API outside the remit of the original client is called, the request is immediately rejected. See scope best practices for a real world example.

    AZURE AD AND MULTIPLE APIs

    For multiple APIs to call each other with Azure AD access tokens, token exchange must be used. This involves each API acting as a client, in which case the client secret you have registered will be used. An example request where the API acts as a client is shown below, from a Node.js sample of mine. Note that the API acting as a client sends its original token as a User Assertion:

    const formData = new URLSearchParams();
    formData.append('grant_type', 'urn:ietf:params:oauth:grant-type:jwt-bearer');
    formData.append('client_id', this._configuration.graphClient.clientId);
    formData.append('client_secret', this._configuration.graphClient.clientSecret);
    formData.append('assertion', accessToken);
    formData.append('scope', this._configuration.graphClient.scope);
    formData.append('requested_token_use', 'on_behalf_of');
    
    const options = {
        url: this._configuration.tokenEndpoint,
        method: 'POST',
        data: formData,
        headers: {
            'content-type': 'application/x-www-form-urlencoded',
            'accept': 'application/json',
        },
        httpsAgent: this._httpProxy.agent,
    };
    
    const response = await axios.request(options as AxiosRequestConfig) as any;
    return response.data.access_token!;