B2C Authenticate User In Web Api - asp.net-core

I have a web app where a user logs in through b2c and have certain claims set once they're logged in. Now, I also need to call an api with javascript when a user clicks a button. So that's all fine but, here's my question:
How would I get the user's claims in the api?
My api and client side web app are both in asp.net core.
Thanks in advance!

At first, you may refer to this document or this official sample to check if it met your requirement.
For the api, it will return user information which is signed in the app.
[HttpGet]
public IEnumerable<Todo> Get()
{
string owner = User.Identity.Name;
return TodoStore.Values.Where(x => x.Owner == owner);
}

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

IdentityServer login without web interaction

I'm currently writing a Xamarin application and I wish to login to my app, this one use IdentityServer 4 for auth.
I wish to avoid any "web" interface for the email/password login, but I'm not sure how to do this (If possible).
So, for now I have a controller with a method like this:
public void LoginAsync(string cliendId, string clientSecret, string email, string password)
{
// Check the clientId & secrets match
....
// Sign in the user
var result = await SignInManager.PasswordSignInAsync(email, password, false, true);
// How to return a token to the Xamarin client here?
}
So, I have 2 questions please:
first I directly check the clientId & secret, is it the way to go? Or should I get a token, then protect this API, and then access it with the token.
how can I generate a new token for the Xamarin client to access the other APIs?
Thanks
Using OpenID Connect you are supposed to open a new web interface and let the user login through that page. Creating your own login/password form is a bad for many reasons. One is that the user does not now where his credentials will end up. Your application should never touch or see the users username/password.
If there is no user involved, then you can look at using the client credentials flow that is supported by IdentityServer.

How to receive the current user with create react app -auth template on net core 3 and identity server

I have never used identity server before and I was wondering how to receive the current user info if they have been authenticated using net core 3.
Using the barebones create react app -auth weather app template
in the WeatherForecastController we have the authorize attribute:
[Authorize]
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
With the following endpoint:
[HttpGet]
public IEnumerable<WeatherForecast> Get()
Which will return the correct data if you have logged in and been granted a jwt bearer token.
I would like to create an endpoint that:
1. Allows anonymous access(logged in or not so the authorize attribute needs ditching or overriding)
2. It should then try to authenticate the user
3.If the user can be identified then the endpoint will return the correct data else if the user cannot be identified it will return a subset of the data
I'm struggling to retrieve the user who has made the request information, I believe i'm supposed to use GET /connect/userinfo identityserver endpoint with the bearer token as a parameter but I have no idea how to do this. I would like to access the user_id so I can corrispond with the identityUser table.
I have also tried:
var identity = (ClaimsIdentity)User.Identity;
IEnumerable<Claim> claims = identity.Claims;
It just feels like theres a proper way to do this stuff that i'm missing. I'd be grateful for someone to point me in the right direction.
It should then try to authenticate the user
Browsers communicate with web applications. You should have a web application to be able to redirect back to IDS4 to login. What you are have is an API. Later Web applications communicate with web APIs. Read more here
If the user can be identified then the endpoint will return the correct data else if the user cannot be identified it will return a subset of the data
What you are looking for is Authorization, you can manually do this check on the endpoint. You can access current user using ControllerBase.User ass a result of passing a valid JWT tokens to the API. Considering you already setup authentication using code like bellow:
services.AddAuthentication("Bearer").AddJwtBearer("Bearer",
options =>
{
options.Authority = "http://localhost:5000";
options.Audience = "api1";
options.RequireHttpsMetadata = false;
options.TokenValidationParameters = new TokenValidationParameters()
{
NameClaimType = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier"//To set Identity.Name
};
});
I'm struggling to retrieve the user who has made the request information
You can simply use ControllerBase.User to get the current authenticated user on API, no need to call any endpoint.
Update: Setting NameClaimType as posted on code above will set Identity.Name to the user's identifier.

OpenID Connect server. Generate access token based on 3-rd party token (social login)

I had implemented OpenID Connect server that generates access tokens for mobile client based on username/password using OpenIddict.
My next goal was to provide ability to generate Access Token using 3-rd party tokens (social login for example), and I started from integration with Google token, but stuck as cannot find any samples/informations about how to do this.
The only one idea that I currently have is to make request to "/connect/token" endpoint and send Google token in "code" parameter, for example in "google:" format, then override OpenIdConnectServerProvider.DeserializeAuthorizationCode method:
Called when receiving an authorization code. An application may use this context to deserialize the code using a custom format and to skip the default logic using
So I have created own CustomProvider class based on OpenIddictProvider, registered it
services.AddOpenIddict<ApplicationUser, ApplicationRole, ApplicationDbContext, int>()
.Configure(builder =>
{ builder.Provider = new CustomProvider(sp.GetRequiredService<SignInService>()); }
and overridden the DeserializeAuthorizationCode method:
public override async Task DeserializeAuthorizationCode(DeserializeAuthorizationCodeContext context)
{
string code = context.Request.Code;
if (code.StartsWith("google:"))
{
string token = code.Replace("google:", "");
var principal = new GoogleTokenValidator().ValidateToken(token, null).Result;
var ticket = new AuthenticationTicket(principal, new AuthenticationProperties(), "Bearer");
ticket.SetPresenters(context.Request.ClientId);
context.Ticket = ticket;
context.Ticket.Properties.ExpiresUtc = DateTime.UtcNow.AddDays(1);
context.HandleResponse();
await _signInService.Login(principal);
return;
}
else
{
base.DeserializeAuthorizationCode(context);
}
}
where GoogleTokenValidator is a custom class for Google token handling (it makes call to Google User Information Endpoint and generate ClaimsPrincipal), based on "copy-pasted" code from GoogleHandler class in aspnet/Security repo.
In general it is working with some additional hacks, but I have strong feeling that reinventing the wheel...
In general it is working with some additional hacks, but I have strong feeling that reinventing the wheel...
You're not only reinventing the wheel, but you're also implementing something totally non-standard that is not supported (at all) by OpenIddict.
Here's the approach I recommend (which is the one we use in the MVC server sample):
The OAuth2/OpenID Connect client application redirects the user agent to your authorization controller (you can take a look at this controller for an example).
OpenIddict will validate the authorization request and allow your controller to be invoked if it's fully valid.
If the user is not already logged in, your authorization controller will redirect the user to the login endpoint, provided by AccountController. At this point, you're free to propose local authentication (e.g using a username/password couple) or Google authentication (you can use the Google authentication middleware for that). You can even offer 2-FA as part of this login process.
Once the user is logged in (e.g after a registration process and/or an external authentication association), his/her browser is redirected back to the authorization endpoint, where a consent form indicating he/she's about to allow your JS app to access his personal data on his/her behalf is displayed.
When the user allows your client application to access his data, the request is handled by your authorization controller, that calls SignInAsync to inform OpenIddict that an authorization code/access token should be returned to your application.

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;
}
});
}