Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support EasyAuth #33

Closed
davidebbo opened this issue Feb 25, 2016 · 89 comments
Closed

Support EasyAuth #33

davidebbo opened this issue Feb 25, 2016 · 89 comments
Assignees
Milestone

Comments

@davidebbo
Copy link
Contributor

davidebbo commented Feb 25, 2016

To make it possible for the Functions to perform authorization, we should pass the various auth claims into the context object.

Idea is to allow users to specify a new "user" auth level value to indicate that a function requires an EasyAuth validated identity.

@christopheranderson christopheranderson added this to the backlog milestone Mar 12, 2016
@mathewc mathewc changed the title Include auth claims in token Support EasyAuth Mar 22, 2016
@mathewc
Copy link
Member

mathewc commented Mar 22, 2016

Retitling

@mattchenderson
Copy link
Contributor

Update here: investigated with @fabiocav on Friday and confirmed that .NET functions can just rely on the ClaimsPrincipal, per the code below. Other languages would have to rely on the headers, and that is not code we should make users write. Would be nice to expose as part of the context object or similar.

using System.Net;
using System.Threading;
using System.Security.Claims;

public static void Run(HttpRequestMessage req, TraceWriter log)
{
    if (!Thread.CurrentPrincipal.Identity.IsAuthenticated)
    {
        log.Info("Not authenticated");
        return;
    }

    ClaimsIdentity identity = (Thread.CurrentPrincipal as ClaimsPrincipal)?.Identity as ClaimsIdentity;
    foreach (var claim in identity.Claims)
    {
       log.Info($"{claim.Type} = {claim.Value}");
    }
}

@mathewc
Copy link
Member

mathewc commented Aug 23, 2016

@mattchenderson I assume both C# and F# will now work, since we just merged the new F# model. For Node, can you give some examples of what needs to be exposed? Is it just things like context.identity.isAuthenticated and context.identity.claims? I.e. can we simply translate the identity into a json object?

Also, we'd then start exposing one more enum value for our existing authLevel property: "user", to indicate that a function requires an authenticated identity. I've already made a provision for that in our enum in anticipation of this :)

@fabiocav
Copy link
Member

@mathewc we may also want to consider making the identity information available as named inputs. Thoughts on that?

@mathewc mathewc self-assigned this Jun 16, 2017
@mathewc mathewc modified the milestones: Next - Triaged, Sprint 1 Jun 28, 2017
@paulbatum paulbatum modified the milestones: Sprint 1, Sprint 3 Jul 12, 2017
@mathewc mathewc modified the milestones: Sprint 4, Sprint 3 Aug 9, 2017
@paulbatum paulbatum modified the milestones: Sprint 5, Sprint 4 Aug 10, 2017
@mathewc mathewc modified the milestones: Sprint 6, Sprint 5 Aug 28, 2017
@paulbatum paulbatum modified the milestones: Next, Sprint 6 Sep 6, 2017
@mathewc mathewc closed this as completed Oct 24, 2017
@mathewc mathewc reopened this Nov 30, 2017
@ConnorMcMahon
Copy link
Contributor

@myusrn I can get the sample working with the following authentication methods

  1. In browser server-directed flow
  2. Bearer token with an AAD access token for EasyAuth
  3. X-ZUMO-AUTH session token. You can generate a session token by hitting /.auth/login/aad with a POST request with the following body
    { "id_token": "", "access_token": "" }

@myusrn
Copy link

myusrn commented Dec 5, 2018

@ConnorMcMahon thanks for clarifications supported authentication methods that helps.

@johndowns thanks for finding and mentioning the bug you found on this thread as that was what was causing me to get the unexpected IsAuthenticated = false and Identity: (, , ) results. Once i set in portal function | Integrate | Authorization = Anonymous -> Function and included code in request i started getting expected IsAuthenticated = true and Identity: (aad, robertob@mymsdn.onmicrosoft.com, Rf-5wbc2D1QRXm0s9VlJfzmMjt-rQ3IYSp6aIjWAbr0);Identity: (WebJobsAuthLevel, Function, default) results.

Please share issue that gets created to track work on this related bug that really can leave a person confused as to why things seem to not be working.

Also note that when i did launched and f5 localhost debug of my vs17 functions project this afternoon it pulled down a new %localappdata%\azurefunctionstools\releases\2.14.0\cli\func.exe replacing the previous \2.11.3\cli\func.exe . The output window showed Function Runtime Version: 2.0.12210.0 implying that local debugging now has required 2.0.12210.0+ runtime version in place as well. I tested (. . . , ClaimsPrincipal principal) parameter injection in the function signature and it was okay with that and does execute with an IsAuthenticated = true identity present but no an authenticated user one, GetIdentityString() output was Identity: (WebJobsAuthLevel, Admin, ). Perhaps this is addressed by calling localhost debug instances using unit test or desktop app with Authorization header bearer token secured call vs browser/user agent session access_token cookie secured call.

@johndowns
Copy link

@ConnorMcMahon I have opened #3857 as requested.

@myusrn
Copy link

myusrn commented Dec 6, 2018

Anyone had succes this easyAuth authentication of request using desktop app / postman request using function authZ code and authorization header bearer token to authN request, i.e. what @ConnorMcMahon referred to as 2. Bearer token with an AAD access token for EasyAuth above?

I ask because i'm now testing that target scenario and am getting 401 Unauthorized against same setup that works if I use browser/user agent request with function authZ code and session access_token cookie secured request, i.e. what @ConnorMcMahon referred to as 1. In browser server-directed flow above.

I was able to use X-ZUMO-AUTH header, i.e. what @ConnorMcMahon referred to as option 3. X-ZUMO-AUTH session token acquired by hitting /.auth/login/aad with a POST request and body { "id_token": "", "access_token": "" } outlined above but using /.auth/login/aad GET that returned me to .auth/login/done#token=<json containing authentication_token / X-ZUMO-AUTH value> query string encoded result. I pulled the authenticationToken from that result and attached it to function request with function authZ code and X-ZUMO-AUTH header containing that session token. While this was reassuring seeing it work I really need option 2 authorization header bearer token story to work so I can acquire tokens using Microsoft authentication library [msal] in wpf/uwp desktop app for token acquisition and refreshes.

@ConnorMcMahon
Copy link
Contributor

@myusrn, Could you share your function app name? You can share it privately by following these instructions. I can help you investigate why scenario 2 is not working for you. I just tested it the other day and it worked for my application.

@myusrn
Copy link

myusrn commented Dec 6, 2018

@ConnorMcMahon,

Below is log streaming ouput guids from calling my functions app using browser openid connect session cookie secured request.

2018-12-06T19:18:27.230 [Information] Executing 'Functions.HttpTrigger1' (Reason='This function was programmatically called via the host APIs.', Id=3b1f14e5-f67e-4289-a297-4e914c7c4fdc)
. . . 
2018-12-06T19:18:27.232 [Information] Executed 'Functions.HttpTrigger1' (Succeeded, Id=3b1f14e5-f67e-4289-a297-4e914c7c4fdc)

In my wpf app where i'm using using azuread v2 compliant msft authentication library [msal] vs azuread v1 compliant azure ad authentication library [adal], nuget to acquire access token used in Authorization header Bearer <access token> secured api requests I have scopes set to scopes = new string[] { "https://myfunctionsapp.azurewebsites.net/user_impersonation" } which does produce token with "scp" claim set to "user_impersonation". I expect that doesn't help you repro in your desktop/mobile test client unless you also have the azuread provisioned clientId and tenantId.

I'm fine with granting your work or msa account access to my resource group and associated azuread if you need ability to look closer at simple inportal + easyauth & express security setup and/or the desktop/mobile client app entry I have in azuread that has api permissions configured to allow requesting token with scope setting that aligns with azure function api its trying to call. Can send me message at myusrn@outlook.com .

@bryans2k
Copy link

bryans2k commented Dec 6, 2018

Anyone had succes this easyAuth authentication of request using desktop app / postman request using function authZ code and authorization header bearer token to authN request, i.e. what @ConnorMcMahon referred to as 2. Bearer token with an AAD access token for EasyAuth above?

I ask because i'm now testing that target scenario and am getting 401 Unauthorized against same setup that works if I use browser/user agent request with function authZ code and session access_token cookie secured request, i.e. what @ConnorMcMahon referred to as 1. In browser server-directed flow above.

I was able to use X-ZUMO-AUTH header, i.e. what @ConnorMcMahon referred to as option 3. X-ZUMO-AUTH session token acquired by hitting /.auth/login/aad with a POST request and body { "id_token": "", "access_token": "" } outlined above but using /.auth/login/aad GET that returned me to .auth/login/done#token=<json containing authentication_token / X-ZUMO-AUTH value> query string encoded result. I pulled the authenticationToken from that result and attached it to function request with function authZ code and X-ZUMO-AUTH header containing that session token. While this was reassuring seeing it work I really need option 2 authorization header bearer token story to work so I can acquire tokens using Microsoft authentication library [msal] in wpf/uwp desktop app for token acquisition and refreshes.

Try this link: https://www.bruttin.com/2017/11/21/azure-api-postman.html

It shows you how to use postman correctly.

@myusrn
Copy link

myusrn commented Dec 6, 2018

@ConnorMcMahon, Things working now using desktop/mobile app authorization header bearer token secured requests.

The issue and fix for me was to ensure that acquired token has audience "aud" claim was set to the application (client) id guid of the functions app azuread registered application.

When using new azuread v2 endpoints compatible microsoft authentication library [msal] I was providing the scope parameter that matched what shows up in registered app's api permission settings. Turns you can arbitrarily configure the starting part of that scope argument to be whatever you want the issued tokens audience claim to contain. So the following did the trick.

//var scopes = new string[] { "https://azfndn1ipt.azurewebsites.net/user_impersonation" }; // matching azfndn1ipt express provisioned entry
var scopes = new string[] { "5270888e-273e-4bba-9283-872088abe9f8/user_impersonation" }; // arbitrarily assigned value to first part of scope which is set to app(client)id of function app registration
authResult = await app.AcquireTokenSilentAsync(scopes, accounts.FirstOrDefault());

@myusrn
Copy link

myusrn commented Dec 7, 2018

@bryans2k thank you for the postman article reference on how to enable oauth token acquisition from azuread endpoints within postman as I've always been acquiring token using scratch app with adal -> msal calls and then pasting the result into postman request to test calls versus less flexible scratch app httpClient requests.

I was able to successfully use it to acquire token matching adal/msal produced versions by using the same desktop/mobile app azuread app registration with required scope claim of "function app id/user_impersonation" with only change being need to add logical authentication | redirect uris such as "https://clientapp/auth".

Not sure if i'll ever need the X-ZUMO-AUTH token, for scenarios other than when trying to debug if its working when oauth bearer token is not, but if that arises i'm curious if you've also been able to configure postman authorization token acquisition to carry out the /.auth/login/aad GET followed extraction of access token from the .auth/login/done#token=<json containing authentication_token / X-ZUMO-AUTH value> url?

@bryans2k
Copy link

bryans2k commented Dec 7, 2018

@myusrn I've never tried but I think you'd have to change the callback to your own website to log the callback from AAD. I don't think Postman offers an option to pass-through the callback.

@maryammadzadeh
Copy link

Hi there,

When adding the ClaimsPrincipal to the function:
public static async Task<IActionResult> RunAsync([HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = null)]HttpRequest req, ILogger log, ClaimsPrincipal principal)

We have our roles defined in AAD and can clearly see the roles in the req param's bearer token.

However, the principal param is only populated with
{
"type": "WebJobsAuthLevel",
"level": "Admin"
}

Our roles are not defined there. We have tried using all AuthorizationLevel, though documentation says to use Anonymous.

Any updates on this issue?

@ConnorMcMahon
Copy link
Contributor

@maryammadzadeh, do you have App Service Authentication / Authorization enabled? Unfortunately, at this time the ClaimsPrincipal does not take Bearer tokens unless you have configured that feature with AAD.

@JoshCollinsMSFT
Copy link

@ConnorMcMahon is it possible to test this functionality locally? Or do we have to deploy to get AAD auth integration to flow the claims principals of bearer tokens?

@ConnorMcMahon
Copy link
Contributor

In the next month or two we are planning on releasing ways to test EasyAuth locally with the Functions CLI. The work on the CLI is actually already done, but we are working on giving the Linux and Windows version of EasyAuth parity so that the local development experience matches up exactly with the production experience.

@JoshCollinsMSFT
Copy link

Appreciate the response. Could you create a link between the PR enabling that functionality and this issue? Thanks!

@myusrn
Copy link

myusrn commented Jan 8, 2019

@JoshCollinsMSFT [ & @ConnorMcMahon ] in the mean time I was able to make use of "unit testing azure functions v2" -> https://medium.com/@tsuyoshiushio/writing-unit-test-for-azure-durable-functions-80f2af07c65e guidance to enable localhost unit tests of function app with injected principal containing test case relevant identity and claims.

See https://github.com/myusrn/lnsexploration/blob/master/xUnit.Tests/FunctionControllerUnitTest.cs and https://github.com/myusrn/lnsexploration/blob/master/xUnit.Tests/AzFuncApp1WebApp1UnitTests.cs for my implementation.

For localhost integration tests I wrote my production implementation with the following types of logic "if (principal.IsInRoleFuncApp("SomeRbacAuthZRole") || req.Host.Value == "localhost:7071")" to cover that case until things Conner & Co are working on get released .

@ConnorMcMahon
Copy link
Contributor

@JoshcolllinsMSFT, Azure/azure-functions-core-tools#789

@maryammadzadeh
Copy link

@ConnorMcMahon I do have app service authN/authR enabled. I think I am just waiting for your MR to complete. I verified that AuthorizationLevel.Function works with a function key. When AuthorizationLevel.Anonymous work, then I should see my claims in the principal.

@Xylez01
Copy link

Xylez01 commented Apr 28, 2019

In the next month or two we are planning on releasing ways to test EasyAuth locally with the Functions CLI. The work on the CLI is actually already done, but we are working on giving the Linux and Windows version of EasyAuth parity so that the local development experience matches up exactly with the production experience.

Is this something that is already released? Currently working on a project that uses azure function authentication but there are some key differences when developing locally.

My claims when the function is deployed:

[
  {
    "type": "stable_sid",
    "value": "sid:<id>"
  },
  {
    "type": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier",
    "value": "sid:<id>"
  },
  {
    "type": "http://schemas.microsoft.com/identity/claims/identityprovider",
    "value": "twitter"
  },
  {
    "type": "ver",
    "value": "3"
  },
  {
    "type": "nbf",
    "value": "1555961219"
  },
  {
    "type": "exp",
    "value": "1558553219"
  },
  {
    "type": "iat",
    "value": "1555961219"
  },
  {
    "type": "iss",
    "value": "<host>"
  },
  {
    "type": "aud",
    "value": "<host>"
  }
]

vs the claims I have when making a request against localhost:

[
    {
        "type": "http://schemas.microsoft.com/2017/07/functions/claims/authlevel",
        "value": "Admin"
    }
]

So right now it's very cumbersome to develop locally.

@ConnorMcMahon
Copy link
Contributor

The work was slightly de-prioritized, so it is not out yet unfortunately. I don't believe there is too much work to be done to finalize that work item yet, but at this time I can't give an ETA for that feature unfortunately.

One way you could test you code changes locally would be to create a second function app with the same exact Authentication/Authorization config that your production function app uses. This function app would that uses function proxies to route requests to your local app. You would likely need to expose your local app to the internet using something like ngrok. Then, requests made to that proxy function app would go through the authentication process and populate the proper headers to populate that user identity before hitting your local code.

I understand the above method is very clunky and not ideal, but unfortunately that is probably the best option until we get this feature out the door.

@Xylez01
Copy link

Xylez01 commented Apr 30, 2019

@ConnorMcMahon thanks for your message! However, that sounds overly complicated 😅.

Right now I'm using a System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler to get the claims from the X-ZUMO-AUTH token when I see there is only one claim that is http://schemas.microsoft.com/2017/07/functions/claims/authlevel. Not a fan of having "debug" code in the production codebase, but it will have to do for now.

One slight issue with that solution is that the claims don't match completely. The http://schemas.microsoft.com/identity/claims/identityprovider claim type when parsing the jwt token is not present, but there is claim of typeidp that has the same value.

@Francisco-Gamino
Copy link
Contributor

Hello @JordyLangen, EasyAuth is supported in both V1 and V2. Please open a new issue if you have a scenario which is not working. Thanks.

@Francisco-Gamino
Copy link
Contributor

Closed issue by mistake, which has already been triaged and moved to the backlog milestone. Reopening...

@fabiocav
Copy link
Member

Closing this issue as it has been repurposed a few times and it is now hard to follow. The original feature tracked by the issue has been completed. For any other issues around the feature, please open new issues.

@Azure Azure locked and limited conversation to collaborators Jul 15, 2019
maiqbal11 pushed a commit that referenced this issue Nov 1, 2019
…Manager, and fetch connectionStrings from Azure on settings fetch. Closes #33, Closes #46
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests