microsoft / azure-container-apps

Roadmap and issues for Azure Container Apps
MIT License
365 stars 29 forks source link

Easy Auth doesn't supply tokens blocking use of Graph API #995

Open johnnyreilly opened 11 months ago

johnnyreilly commented 11 months ago

This issue is a: (mark with an x)

Issue description

First of all, I love Container Apps and I love Easy Auth - thank you for your work on them! ❤️

I'm a long time user of Easy Auth and was delighted when it landed in ACAs. I uncovered an issue early on around how to get .NET to recognise the X-MS-CLIENT-PRINCIPAL header that is supplied with Easy Auth and wrote up a workaround: https://johnnyreilly.com/azure-container-apps-easy-auth-and-dotnet-authentication

This has served me well, however I'm now trying to make use of the Graph API on behalf of my logged in user. I cannot.

Reading the docs I believe I should be trying to use the "on behalf of" approach:

var scopes = new[] { "https://graph.microsoft.com/.default" };

// Multi-tenant apps can use "common",
// single-tenant apps must use the tenant ID from the Azure portal
var tenantId = "common";

// Values from app registration
var clientId = "YOUR_CLIENT_ID";
var clientSecret = "YOUR_CLIENT_SECRET";

// using Azure.Identity;
var options = new OnBehalfOfCredentialOptions
{
    AuthorityHost = AzureAuthorityHosts.AzurePublicCloud,
};

// This is the incoming token to exchange using on-behalf-of flow
var oboToken = "JWT_TOKEN_TO_EXCHANGE";

var onBehalfOfCredential = new OnBehalfOfCredential(
    tenantId, clientId, clientSecret, oboToken, options);

var graphClient = new GraphServiceClient(onBehalfOfCredential, scopes);

The problem is, there's not JWT token that we can make use of in the headers. This is supported in App Service which has a token store and supports the following headers:

X-MS-TOKEN-AAD-ID-TOKEN
X-MS-TOKEN-AAD-ACCESS-TOKEN
X-MS-TOKEN-AAD-EXPIRES-ON
X-MS-TOKEN-AAD-REFRESH-TOKEN

If there was a populated X-MS-TOKEN-AAD-ACCESS-TOKEN then using the "on behalf of" approach above would work. As there are not such headers in Easy Auth with Azure Container Apps, this is not possible. To be clear X-MS-CLIENT-PRINCIPAL is not a JWT - it's a base 64 encoded JSON representation of the principal.

There's definitely some overlap between this issue and https://github.com/microsoft/azure-container-apps/issues/479 - it's not clear to me that they are exactly the same issue though; so I've raised this instead. I'll copy the content across to the other in case this a dupe in which case this can be closed.

Steps to reproduce

Add a controller to your app to surface headers:

    [HttpGet("api/headers")]
    public async Task<IActionResult> GetHeaders()
    {
        var headers = new Dictionary<string, string>();
        foreach (var header in Request.Headers) {
            if (headers.Keys.Contains(header.Key))
            {
                headers[header.Key] = header.Value.FirstOrDefault() ?? "";
            }
            else
            {
                headers.Add(header.Key, header.Value.FirstOrDefault() ?? "");
            }
        }
        return Ok(headers);
    }

Take a look at what comes out.

Expected behavior [What you expected to happen.]

Going to /api/headers surfaces headers which include the following:

X-MS-TOKEN-AAD-ID-TOKEN
X-MS-TOKEN-AAD-ACCESS-TOKEN
X-MS-TOKEN-AAD-EXPIRES-ON
X-MS-TOKEN-AAD-REFRESH-TOKEN

i.e. JWT tokens in headers that can be consumed and used.

Actual behavior [What actually happened.]

Headers do not include the hoped for tokens. These are the headers I encountered:

[
    "Accept",
    "Host",
    "User-Agent",
    "Accept-Encoding",
    "Accept-Language",
    "Cookie",
    "traceparent",
    "Upgrade-Insecure-Requests",
    "Content-Length",
    "sec-ch-ua",
    "sec-ch-ua-mobile",
    "sec-ch-ua-platform",
    "DNT",
    "sec-fetch-site",
    "sec-fetch-mode",
    "sec-fetch-user",
    "sec-fetch-dest",
    "x-forwarded-for",
    "x-envoy-external-address",
    "X-Request-ID",
    "x-envoy-expected-rq-timeout-ms",
    "x-k8se-app-name",
    "x-k8se-app-namespace",
    "x-k8se-protocol",
    "x-k8se-app-kind",
    "x-ms-containerapp-name",
    "x-ms-containerapp-revision-name",
    "x-arr-ssl",
    "x-forwarded-proto",
    "X-MS-CLIENT-PRINCIPAL-NAME",
    "X-MS-CLIENT-PRINCIPAL-ID",
    "X-MS-CLIENT-PRINCIPAL-IDP",
    "X-MS-CLIENT-PRINCIPAL"
]
njuCZ commented 11 months ago

Hi all, now containerapp has supported token store feature since api-version 20230502preview. users can provide a blob storage sas url (need contain enough permission) to containerapp secret (name could be arbitrary) and then use following command:

az containerapp auth update -g myResourceGroup -n cotanierappName --token-store true --sas-url-secret-name {secretName}

After that, we can get those headers from incoming request: X-MS-TOKEN-AAD-ID-TOKEN X-MS-TOKEN-AAD-ACCESS-TOKEN X-MS-TOKEN-AAD-EXPIRES-ON X-MS-TOKEN-AAD-REFRESH-TOKEN

and /.auth/me and /.auth/refresh can work fine

johnnyreilly commented 11 months ago

Thanks for sharing @njuCZ! Is there any documentation on this? Do you know if a more plug and play version of this is likely to become available? I think that App service etc don't require that you have to manually set up a storage sas URL etc?

njuCZ commented 11 months ago

Hi @johnnyreilly, app service supports two types of token store feature: default shared file system token store and blob storage sas url token store. However, containerapp currently only supports blob storage sas url token store. There is no document on this currently, I will work with PM for the official document

johnnyreilly commented 11 months ago

Thanks @njuCZ. Do you know if app service will support an equivalent to default shared file system in future?

Also, in my world we're a Bicep shop - we deploy everything with Bicep. If the blob storage SAS URL token store is the only option, would you be able to provide documentation on how to use it with that? Thanks!

njuCZ commented 11 months ago

@johnnyreilly currently there is no plan to support the default shared file system token store feature. after the document is ready, I will share here. You could use the azure cli command I provided to enable it

johnnyreilly commented 11 months ago

Thank you!

ltrain777 commented 6 months ago

Hi all, now containerapp has supported token store feature since api-version 20230502preview. users can provide a blob storage sas url (need contain enough permission) to containerapp secret (name could be arbitrary) and then use following command:

az containerapp auth update -g myResourceGroup -n cotanierappName --token-store true --sas-url-secret-name {secretName}

After that, we can get those headers from incoming request: X-MS-TOKEN-AAD-ID-TOKEN X-MS-TOKEN-AAD-ACCESS-TOKEN X-MS-TOKEN-AAD-EXPIRES-ON X-MS-TOKEN-AAD-REFRESH-TOKEN

and /.auth/me and /.auth/refresh can work fine

@njuCZ I'm using 2023-08-01-preview based on the deployment arm templates in the resource group. I've added the --token-store and the blob sasUrl.

{
"type": "Microsoft.App/containerApps/authConfigs",
"apiVersion": "2023-08-01-preview",
....
"login": {
                    "routes": {},
                    "tokenStore": {
                        "enabled": true,
                        "azureBlobStorage": {
                            "sasUrlSettingName": "token-store-sas-url"
                        }
                    },
                    "preserveUrlFragmentsForLogins": false,
                    "cookieExpiration": {},
                    "nonce": {}
                },
...
}

I'm still not able to see the X-MS-TOKEN-AAD-ACCESS-TOKEN header.

  1. What are the specific permissions on the sas url? I've given all permissions (every checkbox is checked) and used the url to the blob endpoint. image

  2. Has there been a regression and I should use the 20230502preview version specifically to get the access token headers to show up?

  3. Are there specific logs in the EasyAuth container that I could look at to debug further?

Thanks,

njuCZ commented 6 months ago

Hi @ltrain777, it seems you provide the wrong sas url, you could follow the doc here: https://learn.microsoft.com/en-us/azure/container-apps/token-store This should be the right portal for getting a blob storage sas url . image

ltrain777 commented 6 months ago

@njuCZ Thank you for pointing me to the correct documentation. I updated the secret with the sas url to the private container sas url. I created another revision and updated the container. I'm still not getting the x-ms-token headers. Are there debugging logs that might help me find the issue?

One interesting difference I found was that when token-store is true I don't get 404 when going to /.auth/me. That endpoint now returns empty array. The /.auth/refresh endpoint gives me 403.

njuCZ commented 6 months ago

@ltrain777 you should see some error logs in your console logs if you have configured the logging. There should be nothing special, several points to check:

  1. check the secret value is correct sas url.
  2. enable easyauth and enable tokenstore. you can use azure cli to show the right values.

If you still encountered issue, you could send email to acasupport at microsoft.com with your containerapp info, then we could take a check for you.

johnnyreilly commented 6 months ago

I think this part of the documentation may be incorrect:

Additionally, you can create your store using an ARM template

This doesn't appear to be an ARM template reference. Is there an ARM / Bicep template reference available please?

njuCZ commented 6 months ago

Hi @johnnyreilly thanks for reporting it, we will fix the link soon.

anthonychu commented 6 months ago

Docs will be updated later today with the correct link to the ARM reference: https://learn.microsoft.com/en-us/rest/api/containerapps/container-apps-auth-configs/create-or-update?view=rest-containerapps-2023-11-02-preview&tabs=HTTP#blobstoragetokenstore

johnnyreilly commented 6 months ago

Hi @anthonychu - forgive me if I'm getting the wrong end of the stick, but I don't think that's an ARM template?

The original message I quoted:

Additionally, you can create your store using an ARM template

... has been edited out of the original comment above, is that because there isn't an ARM template reference available to share?

ltrain777 commented 6 months ago

@ltrain777 you should see some error logs in your console logs if you have configured the logging. There should be nothing special, several points to check:

  1. check the secret value is correct sas url.
  2. enable easyauth and enable tokenstore. you can use azure cli to show the right values.

If you still encountered issue, you could send email to acasupport at microsoft.com with your containerapp info, then we could take a check for you.

@njuCZ I had too many sas urls generated on my scratchpad and used the incorrect one with only read permissions to the container. I used the correct sas url with read, write, delete and I'm now able to get the access tokens. Thank you for your help!

petr-stupka commented 5 months ago

Hi @njuCZ token store make life so much easier. All working fine, except i'm not able to get the X-MS-TOKEN-AAD-REFRESH-TOKEN header.

I also tried to add OAuth permissions to the SPN (where the offline_access grant the access for refresh token if i'm not wrong)

image

Also another challenge is /.auth/me endpoint. The endpoint is not accessible using the Container App URL (make sense as it it then public traffic) except i will send the cookie to authenticate, but how i can access it from the container itself (server)?

Thank you

hbuckle commented 2 weeks ago

Any plans to allow access to the token store storage account using managed identity, rather than having to manage SAS?

njuCZ commented 1 week ago

Hi @hbuckle , we are going to release this feature soon. Once it's available, we will update here.