We have an app registration within our Azure subscription that requires access to Graph, set up under the "Required permissions" section.
Our Angular application is kicking off authentication against AAD, receiving the JWT, appending it to all requests in the Header and sending it to our WebApi where it's being validated successfully....great!
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6IjlGWERw...
Now, from the WebApi, I also want to make a request to Graph and considering I have the validated token, I'm passing it to a Graph REST API via an Authorization header.
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", jwtToken);
However, I'm getting a 401 (Unauthorized) result. Why? Obviously I'm missing a piece of the puzzle, but I don't know what.
I assumed that by setting up Graph permissions in the Azure app registration that when it sent the token to our Angular app it was valid for both AAD and Graph...but it seems this is not the case.
My JWT looks like the following. Note the "aud" only shows my app registration id and not Graph (if that's the problem, how do I fix it?)
{
"aud": "<application id here>",
"iss": "https://sts.windows.net/<tenant id here>/",
"iat": 1499121655,
"nbf": 1499121655,
"exp": 1499125555,
"aio": "ASQA2/8DAAAACuKzzzni2uaVoaIb9yJa4j3XuG0O+9cQQQlnqXl8Sr0=",
"amr": [
"pwd",
"mfa"
],
"family_name": "Smith",
"given_name": "John",
"name": "John Smith",
"nonce": "<nonce here>",
"platf": "3",
"pwd_exp": "941678",
"sub": "<sub here>",
"tid": "<tenant id here>",
"unique_name": "[email protected]",
"upn": "[email protected]",
"ver": "1.0"
}
EDIT
I've now implemented the on-behalf-of code as Nan Yu described, but I am still getting a 401. The front-end token is being validated in the WebAPI, which is then creating a request to Graph.
string authString = ConfigurationManager.AppSettings["ida:Authority"];
string clientId = ConfigurationManager.AppSettings["ida:Audience"];
string clientSecret = ConfigurationManager.AppSettings["ida:AppSecret"];
string aadInstance = ConfigurationManager.AppSettings["ida:AADInstance"];
string tenant = ConfigurationManager.AppSettings["ida:Tenant"];
string graphResourceId = ConfigurationManager.AppSettings["ida:GraphResourceId"];
AuthenticationContext authenticationContext = new AuthenticationContext(authString);
ClientCredential clientCred = new ClientCredential(clientId, clientSecret);
var bootstrapContext = ClaimsPrincipal.Current.Identities.First().BootstrapContext as System.IdentityModel.Tokens.BootstrapContext;
string userName = ClaimsPrincipal.Current.FindFirst(ClaimTypes.Upn) != null ? ClaimsPrincipal.Current.FindFirst(ClaimTypes.Upn).Value : ClaimsPrincipal.Current.FindFirst(ClaimTypes.Email).Value;
string userAccessToken = bootstrapContext.Token;
UserAssertion userAssertion = new UserAssertion(bootstrapContext.Token, "urn:ietf:params:oauth:grant-type:jwt-bearer", userName);
string authority = String.Format(CultureInfo.InvariantCulture, aadInstance, tenant);
string userId = ClaimsPrincipal.Current.FindFirst(ClaimTypes.NameIdentifier).Value;
AuthenticationResult result = await authenticationContext.AcquireTokenAsync(graphResourceId, clientCred, userAssertion);
This works fine because of the token being returned now has the Graph "aud" and "scp" for the user that logged in:
"aud": "https://graph.windows.net",
"scp": "User.Read User.ReadBasic.All",
"unique_name": "[email protected]",
"upn": "[email protected]",
Why am I still unauthorized when trying call the /me Graph API?