Claims based authorization token in Azure B2C and .Net Core 2.0 - asp.net-core

I am building a ASP .Net Core 2.0 app and would like to know how to add the groups claim to my Azure B2C access token on my backend. I use the user's id to query MS Graph to get the user's group claim using ADAL and need the groups on the authorization token every time the user hits a controller. I would rather not query MS Graph every time a controller is hit.
Is it possible to add the groups claim to the B2C token after it is retrieved?
If not, should I store the groups as a Session variable?
If those aren't right, should I craft a second authorization token with the groups and then use that in my header when I send reqeusts?

You can in one of the OpenID Notifications (i.e. OnTokenValidated) and add user's groups(or roles ,but they are different ) to the ClaimsPrincipal. Something like :
options.Events = new OpenIdConnectEvents
{
OnTokenValidated = ctx =>
{
//query the user's groups using api
// add claims
var claims = new List<Claim>
{
new Claim("groups", xxxx-xx-xx)
};
var appIdentity = new ClaimsIdentity(claims);
ctx.Principal.AddIdentity(appIdentity);
return Task.CompletedTask;
},
};
Below links are code sample with .net framework , you can modify to fit the .net core version :
Authorize By Group in Azure Active Directory B2C
Azure AD B2C - Role management
You can support adding group claims to b2c issued tokens by voting for it in the Azure AD B2C feedback forum: Get user membership groups in the claims with Azure AD B2C

Related

How to authorize the users (with specific membership, user group or claims) to my applications by Azure ad b2c?

I have a react app as my client app and an asp.net api as my api. I have managed to integrate Azure ad b2c login into my client app. I can attach the acquired access token (from Azure ad b2c) to a request that will be sent to my api and this works fine. I have access to my api with and can use resources in my api.
[Authorize]
[Route("[Controller]")]
[ApiController]
public class StudentController : Controller
{
[HttpPost]
public async Task<IActionResult> CreateStudent([FromBody] CreateModel model)
{
some functions...
}
}
But my question is that how I can restrict/authorize the users by claims/user group to have access to my api. I know that I can't use application/user role in Azure ad b2c But there are maybe some other solutions by claims and/or user group. I really appreciate any help :)
Please check if below points can give an idea to work.
You can try to add custom attributes in the AADB2C .Later check custom claims in Azure AD B2C where the consumer can select required roles during the signup process which is later returned in the token. Please refer to documentation for more details.
If not , one may need to get group member claims from the Microsoft graph api in code configuration and try to for backend api token in code by retrieving from graph and then authorize.
Something like below (see references for further details) in start up class configureservices method .
ex: Role-based-Authorization- denious/Azure-B2C (github.com)
// get authenticated user ID
var subjectId = identity.FindFirst(ClaimTypes.NameIdentifier).Value;
// query user roles
var client = _serviceProvider.GetRequiredService<MSGraphClient>();
var roles = await client.GetUserRolesAsync(subjectId);
// add roles to identity's claims collection with the right type
foreach (var role in roles)
{
var roleClaim = new Claim(identity.RoleClaimType, role);
identity.AddClaim(roleClaim);
}
Please check below references for work arounds.
Authorize By Group in AAD B2C - Stack Overflow or Azure AD B2C - Role management - Stack Overflow
Add claims into token Azure B2C - Stack Overflow
Using custom claims for Azure AD B2C roles - DEV Community

How to authenticate on behalf of user to access their google calendar information from webjob/azure function?

We have a webapp that will allow users to sync certain events in their calendar to our system. We can have the user login to google and authorize us to read their calendar and profile.
Now in order to sync their events we want to have an azure function, webjob running at certain intervals that connects to their google calendar and based on some logic will add the event information to our db so that the user can view content in our web app.
How can I get the azure function/webjob to authenticate on the users behalf after they have enabled the featured and authorized out app to get access to their calendar?
=======================================
UPDATE:
This is the code snippet example if anyone else needs it.
TokenResponse token = new TokenResponse { RefreshToken = syncUser.GoogleRefreshToken };
UserCredential credentials = new UserCredential(new GoogleAuthorizationCodeFlow(
new GoogleAuthorizationCodeFlow.Initializer
{
ClientSecrets = new ClientSecrets
{
ClientId = "ClientIDValue",
ClientSecret = "ClientSecretValue"
}
}), "user", token);
CalendarListResource.ListRequest request = service.CalendarList.List();
CalendarList calendars = request.Execute();
Assuming you are storing the users refresh tokens and you can have your azure function read from where ever it is you are storing the refresh token. You just need to use the refresh token to request a new access token for the user and you can access their data offline.
Now I am not an Azure expert, but your going to have to ensure that you set up your redirect uri properly so that azure can get the response back from the authorization. You should also set your code to handle the refresh token expiring.
Also GoogleAuthorizationCodeFlow.Initializer supports IDataStore which means you could set up your own implementation of Idatastore and use that to feed it the refresh token from where ever it is you are grabbing it from.

What is the purpose of additionalLocalClaims in Identity Server 4 SignInAsync

In Identity Server 4, Quick Start, ExternalController.cs - CallBack method, I found the following:
// this allows us to collect any additonal claims or properties
// for the specific prtotocols used and store them in the local auth cookie.
// this is typically used to store data needed for signout from those protocols.
var additionalLocalClaims = new List<Claim>();
var localSignInProps = new AuthenticationProperties();
ProcessLoginCallbackForOidc(result, additionalLocalClaims, localSignInProps);
// issue authentication cookie for user
await HttpContext.SignInAsync(user.SubjectId, user.Username, provider, localSignInProps, additionalLocalClaims.ToArray());
You can see the complete set of code at this link - IdentityServer4.Quickstart.UI
I tried to add some claims to that additionalLocalClaims list by doing the following:
additionalLocalClaims.Add(new Claim("TestName", "TestValue"));
But it never appears in the UserClaims or AccessToken even though ClaimType "TestName" is included in ApiResource.
I would like to add some custom claims/values in AccessToken for Google Authentication and I thought additionalLocalClaims is the right one to append additional claims.
P.S. I finally implemented IProfileService and could return the additional claims. But I still want to know what's the use case of that additionalLocalClaims in HttpContext.SignInAsync extension method.
There are authentication-related extension methods on the HttpContext from ASP.NET Core to issue the authentication cookie and sign a user in , and you can add custom cliams to cookie for authentication session of Identity Server 4 .For example , you can add claims which needed for signout , some external idenity provider may issue a session id claim , you can involve it in local auth cookie via additionalLocalClaims , which could be used to perform single sign-out from external identity provider if user logout in your identity server . But the claims are kept in local auth cookie of IDS4 , they are not in ID token ,access token and userinfo endpoint . You can implement IProfileService as you said to involve custom claims in tokens or userinfo endpoint .

Missing Claims from within the IdentityServer Website, including all samples

I am sure this is down to a lack of understanding.
I am trying to access the currently-logged in users claims, within an IdentityServer instance. I am finding that any claims I provide the user are only available to the setup clients, and not the IdentityServer itself.
My issue can be replicated by using any of the quick start samples provided by the IdentityServer4 team (QuickStart Samples)
I am building a site that will provide authentication, using IdentityServer4, and also provide some interface screens to manage your own profile. To facilitate this I will need access to the claims from within the IdentityServer site.
If we look at the test users on the quick starts, we have this user:
new TestUser
{
SubjectId = "1",
Username = "alice",
Password = "password",
Claims = new List<Claim>
{
new Claim("name", "Alice"),
new Claim("website", "https://alice.com")
}
},
We can see it has 2 claims; name and website.
Within the login controller, I also add another claim, just before signing in (by way of experimenting)
user.Claims.Add(new Claim("given_name", "bob"));
// issue authentication cookie with subject ID and username
await HttpContext.SignInAsync(user.SubjectId, user.Username, props);
When the QuickStart site and the MVC Client are running, I can successfully log in. The Secure page then shows me the claims below (after enabling AlwaysIncludeUserClaimsInIdToken)
However, if i visit the Grants section of the IdentityServer4 Quickstart, and inspect the current User I see an entirely different set of claims, shown below:
How, within IdentityServer4 Quickstart, can i access the same list of claims that were returned in the ID Token?
My specific reason is i will be storing an Active Directory UPM as one of the claims and will need access to this when the user is within any secure page in our Identity Server.
Ok - after a day of playing around, I realized there were other overrides for the HttpContext.SignInAsync() method.
Before, I had this - as per tutorial
await HttpContext.SignInAsync(user.SubjectId, user.Username, props);
Changing this to
await HttpContext.SignInAsync(user.SubjectId, user.Username, props, user.Claims.ToArray());
Gives me exactly what i was looking for.
Leaving this here on the off chance others have the same issue.

Azure Mobile Services vs MVC4 SimpleMembership

When using an ASP.Net MVC4 site, it's very easy to add OAuth authentication with SimpleMembership.
OAuthWebSecurity.RegisterTwitterClient(consumerKey,consumerSecret);
When using Azure Mobile Services on a client device, it's very easy to add OAuth authentication.
await App.MobileService.LoginAsync(MobileServiceAuthenticationProvider.Twitter);
The problem is that these are two different data stores. I need to users to be able to register and/or login from either the app or the site. What is the best/easiest way to provide integrated OAuth authentication from devices and an ASP.Net site? Are there samples available?
I was only able to achieve this with Twitter and Facebook logins when Azure Mobile Services and MVC SimpleMembership were in play. Please see this thread which admittedly has a lot to look through, but it does explain my findings in pretty good detail.
http://social.msdn.microsoft.com/Forums/en-US/azuremobile/thread/d54d28c6-6941-4af5-b116-dc8c51820498
Sorry I couldn't give you any code, because my stated goal was to not write any authentication/security code for this integration.
Nate
I just finished posting a sample that uses ASP.NET MVC4 simple membership to authenticate to an Azure Mobile Service (via Facebook, in my example) at http://blogs.msdn.com/b/carlosfigueira/archive/2013/06/25/exposing-authenticated-data-from-azure-mobile-services-via-an-asp-net-mvc-application.aspx. The post contains a lot of details, but the idea is that if you can get the provider access token (from Facebook or Google, for example), you can format it and send to the backing mobile service. In the snippet below, the facebook token was stored in the session state, and is retrieved by a method that ensures that the user is logged in.
if (MobileService.CurrentUser == null)
{
var accessToken = Session["facebooktoken"] as string;
var token = new JObject();
token.Add("access_token", accessToken);
return MobileService.LoginAsync(MobileServiceAuthenticationProvider.Facebook, token).ContinueWith<bool>(t =>
{
if (t.Exception != null)
{
return true;
}
else
{
System.Diagnostics.Trace.WriteLine("Error logging in: " + t.Exception);
return false;
}
});
}