Azure / static-web-apps

Azure Static Web Apps. For bugs and feature requests, please create an issue in this repo. For community discussions, latest updates, kindly refer to the Discussions Tab. To know what's new in Static Web Apps, visit https://aka.ms/swa/ThisMonth
https://aka.ms/swa
MIT License
330 stars 56 forks source link

SWA + Azure Container app. Custom roles management #998

Open ariggi-epam opened 1 year ago

ariggi-epam commented 1 year ago

Describe the bug

A clear and concise description of what the bug is. I'm using aad custom authentication. I'm hosting the backend api on a azure container app. The role management endpoint (the one pointed by the rolesSource config setting in the auth portion) is hosted there too. Following the example I expected to find the user and the access token (with ms graph audience) in the body of the request. I always receive requests with empty body and the following headers 'x-ms-auth-token', Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IjVGNTg2NTM1NEQ0....' 'X-MS-CLIENT-PRINCIPAL-ID', 'anonymous', 'X-MS-CLIENT-PRINCIPAL-IDP', 'azureStaticWebApps',

The bearer token has as audience the container app (instead of ms graph, the one I'd need to call ms graph to get group memberships as in the tutorial). I tried with both v1 and v2 aad configs. Same result.

To Reproduce Steps to reproduce the behavior:

  1. host the backend in the container app
  2. expose an api in the container app to manage the roles (getRoles)
  3. point the rolesSource to that endpoint
  4. login against aad
  5. the request body is empty and the bearer token has the azure container app as aud

staticwebappconfig.json `{ "navigationFallback": { "rewrite": "/index.html" }, "auth": { "rolesSource": "/api/getroles", "identityProviders": { "azureActiveDirectory": { "registration": { "openIdIssuer": "https://login.microsoftonline.com//v2.0", "clientIdSettingName": "AZURE_CLIENT_ID", "clientSecretSettingName": "AZURE_CLIENT_SECRET" }, "login": { "loginParameters": [ "scope=openid profile email https://graph.microsoft.com/User.Read" ] } } } }, "globalHeaders": { "Cache-Control": "no-cache" }, "routes": [ { "route": "/api/albums", "allowedRoles": ["admin"] }, { "route": "/api/alberto", "allowedRoles": ["reader"] }, { "route": "/api/testdeploy", "allowedRoles": ["editor"] }, { "route": "/logout", "redirect": "/.auth/logout", "allowedRoles": ["anonymous", "authenticated"] }, { "route": "", "allowedRoles": ["authenticated"] }

  ],
  "responseOverrides": {
    "401": {
      "statusCode": 302,
      "redirect": "/.auth/login/aad"
    }
  }

}`

swa repo https://github.com/ariggi-epam/my-first-static-web-app

aca repo https://github.com/ariggi-epam/containerapps-albumapi-javascript

Expected behavior Get the user access token in the request body

What am I missing?

annikel commented 1 year ago

Hi @ariggi-epam, Could you please link the example/tutorial you are following?

ariggi-epam commented 1 year ago

Hi @annikel , sure, I found it here, it points to this tutorial. It says it is in preview and it works with a function. I hoped I could use the aca since it seems you can use a "bring your own" function.

annikel commented 1 year ago

@ariggi-epam to extract the client principal object from the header did you follow the example and base 64 decoded the header like described in this docs

alberto-riggi commented 1 year ago

@annikel yes, I was able to get the client principal object once authenticated (although even there, there is an issue I'll talk about later on), but this issue happens before and it's related to the roles management. Right after the user authenticates, it is supposed to call a function (I'd like to use the azure container app in this case) to obtain the roles of the user. This is in preview and it's an alternative to the built in roles management that's based on invitations and allows only 25 users. The request to this function should contains the user in its body, but it arrives empty to the azure container app.

Regarding the other issue on the client principal object I mentioned before, I was trying to circumvent the issue by using the roles that come in the claims (instead of the userroles), but those claims are not propagated in the client principal object (the base64 content of that header). There is already another issue for this other problem.

Hope it makes sense

serena-shen2 commented 1 year ago

Hi @alberto-riggi @ariggi-epam. Would you be comfortable providing us the site's defaultHostname in order to allow us to look into our logs? Feel free to create a support ticket if you do not want to share this information here.

ariggi-epam commented 1 year ago

Hi @serena-shen2 , yes, np. Can I send it by mail?

serena-shen2 commented 1 year ago

@ariggi-epam You would either need to share it here or create a support ticket with Azure. Regardless I am looking into the issue now, being able to look at your site's logs would be an added bonus.

alberto-riggi commented 1 year ago

ok, https://nice-plant-058c69d10-staging.centralus.2.azurestaticapps.net/ . I have disabled the role management. Going to enable it again.

alberto-riggi commented 1 year ago

ready, do you need the aca url too? If I can help in any other ways please let me know.

ariggi-epam commented 1 year ago

Hi @serena-shen2, is there any other way I can help with?

serena-shen2 commented 1 year ago

As of right now no more information is needed from your end, we're currently investigating the issue. I will reach out if I have any other questions regarding your setup, thank you.

knkarthic commented 1 year ago

@annikel yes, I was able to get the client principal object once authenticated (although even there, there is an issue I'll talk about later on), but this issue happens before and it's related to the roles management. Right after the user authenticates, it is supposed to call a function (I'd like to use the azure container app in this case) to obtain the roles of the user. This is in preview and it's an alternative to the built in roles management that's based on invitations and allows only 25 users. The request to this function should contains the user in its body, but it arrives empty to the azure container app.

Regarding the other issue on the client principal object I mentioned before, I was trying to circumvent the issue by using the roles that come in the claims (instead of the userroles), but those claims are not propagated in the client principal object (the base64 content of that header). There is already another issue for this other problem.

Hope it makes sense

Even we are trying to use the function(preview) for custom role management but the GetRoles() api is not even getting called. Do we need to enable the function (preview) is any place in azure or can you share the steps to configure custom roles.

alberto-riggi commented 1 year ago

@knkarthic I do see GetRoles getting called right after the user authenticates (I'm using AAD as provider, but I don't think it matters). I am using Azure Container Apps. The request body is empty though.

To enable it I simply had to add the rolesSource setting in the auth section of the staticwebappconfig.json as shown here. The static web app needs to use the standard plan.

"auth": {
"rolesSource": "/api/getroles",
"identityProviders": {
"azureActiveDirectory": {
"registration": {
"openIdIssuer": "https://login.microsoftonline.com/<your-tenant>/v2.0",
"clientIdSettingName": "AZURE_CLIENT_ID",
"clientSecretSettingName": "AZURE_CLIENT_SECRET"
},
"login": {
"loginParameters": [
"scope=openid profile email https://graph.microsoft.com/User.Read"
]
}
knkarthic commented 1 year ago

@alberto-riggi we have added the rolesource but then when we try to login our app in UI, getRoles() is not actually called. I see in the document it is mentioned as Function(Preview), does it mean we need to enable or register.

alberto-riggi commented 1 year ago

@knkarthic I'm not sure I'm understanding correctly "enable or register". If you are using a managed function, the api should be already linked to your environments. If you are using a "bring your own" function (an existing azure function) as api, you'd need to link the environment to the api, you can do it from the api blade on the portal.

Maybe I'm saying something you already know but with the byo api, you first need to empty the api_location setting in the workflow (given that you are using the github workflow to deploy) to enable the linking setting on the api blade of the azure portal (hope it makes sense).

Once it's linked you just create the function or the api endpoint and it gets called after authentication. I don't remember if I tried with a function. I do have it in my azure app container. I guess it shouldn't be different.

knkarthic commented 1 year ago

@alberto-riggi actually we have created a app and doing the role assignments using azure. We just wanted to do the role management within the app. So when we checked, we came acrosss GetRoles() api to be configured. We did configure it and tried to login into our app but then we were not able to see the GetRoles() getting called. We did the configuration as per the documents provided but no luck. So not sure if we missed anything.

alberto-riggi commented 1 year ago

@knkarthic what is the plan you are using? what api (managed or byo function)? can you post the auth section of the staticwebappconfig?

knkarthic commented 1 year ago

alberto-riggi we are using standard plan. we are using byo function. Auth section of staticwebappconfig: "auth": { "rolesSource": "/api/GetRoles", "identityProviders": { "azureActiveDirectory": { "userDetailsClaim": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name", "registration": { "openIdIssuer": "https://login.microsoftonline.com/", "clientIdSettingName": "AZURE_CLIENT_ID", "clientSecretSettingName": "AZURE_CLIENT_SECRET" }, "login": { "loginParameters": ["resource=https://graph.microsoft.com"] } } }

thomasgauvin commented 1 year ago

Hey folks, following up on this thread.

  1. note that the user credentials are not directly in req.body of the custom roles function, but rather in the req.body.user. This example shows how you can access it: https://github.com/staticwebdev/roles-function/blob/665284067dfe0dc91fcd40769e54ae6a8d1bd877/api/GetRoles/index.js#L10C11-L10C16 I have opened a PR in our docs to fix this

  2. this feature requires the Standard SKU Static Web App, since it requires the configuration of custom authentication.

  3. no need to register/enable in the Portal. To configure this feature, you must follow this doc https://learn.microsoft.com/en-us/azure/static-web-apps/authentication-custom?tabs=aad%2Cfunction#manage-roles. Essentially, you must 1. set the auth.rolesSource function endpoint in the staticwebapp.config.json file, 2. create the function to assign the role. The tutorial given here with the AAD example is correct and I've tested it to work end to end https://learn.microsoft.com/en-us/azure/static-web-apps/assign-roles-microsoft-graph

  4. the rolesSource/custom role function will not be publicly accessible, and will only be called after a successful login with the credentials obtained from the custom auth provider

Hope this helps!