I have an IDP server implemented by Duende IdentityServer assume which is hosted on idp.com and there are two separate ReactJS applications hosted on app.mysite.com and profile.mysite.com and they are using JWT token for authentication and authorization process. now when I login into app.mysite.com through idp.com profile.mysite.com is un unauthenticated and needs another login. I use the same client configuration for both of these sites. I know there are some methods such as using an IFRAME inside client code to share the JWT token between these two app but I am looking for a built-in approach inside the Identity server to solve this issue?
First of all, if you have 2 CLIENTS, you should configure 2 separate configurations for both of them.
Afer separation of clients you should rely on cookie set on idp.com after first authentication. (Good to know - How to setup cookie authentication basic cookie authentication: https://learn.microsoft.com/pl-pl/aspnet/core/security/authentication/cookie?view=aspnetcore-6.0)
Anyway, if you configured IdentityServer properly, it handles cookie authentication "out-of-the-box" - so probably the only thing you have to do is to Signin the user.
AuthenticationProperties props = new AuthenticationProperties
{
IsPersistent = true,
ExpiresUtc = DateTimeOffset.UtcNow.Add(LoginOptions.RememberMeLoginDuration)
};
var issuer = new IdentityServerUser(user.SubjectId)
{
DisplayName = user.Username
};
await HttpContext.SignInAsync(issuer, props);
When the youser want to login to second application, after start of the flow (eg. code flow) and redirect to the idp.com, idp.com knows that the user is already signed-in (cookie) and should immediately generate token and redirect back to the return url.
If you need you can adjust custom behaviours using IProfileService.
I have an API running using Azure AD authentication, with three roles (Admin/Manager/Viewer). On the API side of things, this all works fine, when a request comes in, the roles are all set correctly.
Now I'm building a website that communicates with this API (Currently Blazor server side, but could just as easily be an MVC site). The user logs into this site, and when they perform an action I'm using the On-Behalf-Of flow to acquire a token and call the API.
What is the best way for the website to know what roles the user has on the API? This would be used to hide/disable certain pages.
I see two ways right now:
Duplicate the roles so the web app has the same roles setup, worst case if the web app is missing one, they'll just get an error when executing a protected action
Expose an method on the api to return the users roles from the token and have the web app call this when a user logs in
Am I missing something here?
I figured out a way to do this, once the user logs in, I can acquire an access token for the api and then use the JwtSecurityTokenHandler to read it into a JwtToken and access the claims there.
var accessToken = await _tokenAcquisition.GetAccessTokenOnBehalfOfUser(this._contextAccessor.HttpContext, new[] { _scope });
var handler = new JwtSecurityTokenHandler();
var jwt = handler.ReadJwtToken(accessToken);
//jwt.Claims - get roles from claims
I am trying to get a ASP.NET Core 2.1 web app running with Azure AD B2C.
I have this running after much reseach (and more trial and error) as the instructions found in the docs are a little off.
The sign-in/sign-up process works using the default scaffolding during the project setup wizard, plus the new Microsoft.AspNetCore.Authentication.AzureADB2C.UI Nuget, which resulted in a simplified, yet "back boxed" experience during startup.
The problem currently is that I am unable to make this work with a custom Reply URL that is different from "signin-oidc". I have read that "signin-oidc" is baked into the provider somehow, and is hence the default.
I have a OnboardingController with a Start action defined where I want the user to land after signing up, so I have done the following:
A) I tested that the Url localhost:12345/Onboarding/Start works. The
page is displayed correctly.
B) In appsettings.json I change AzureAdB2C's "CallbackPath": "/signin-oidc" to "CallbackPath": "/Onboarding/Start"
C) I go to the tenant and change the application's Reply URL to localhost:12345/Onboarding/Start. IMPORTANT side note: Unlike in the ADB2C samples and guides, you MUST append the Reply URL with either signon-oidc or a custom request path! Localhost:12345 is NOT ENOUGH!
I can confirm the authentication worked:
Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationHandler:Information: AuthenticationScheme: AzureADB2CCookie signed in.
Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request finished in 56.201ms
Then, when I manually navigate to /Onboarding/Start in the browser, I get
Error from RemoteAuthentication: Correlation failed.
Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request starting HTTP/1.1 GET http://localhost:12345/Onboarding/start
Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectHandler:Warning: .AspNetCore.Correlation. state property not found.
Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectHandler:Information: Error from RemoteAuthentication: Correlation failed..
1.
But I am never redirected to the intendend /Start page. instead I land back on the root homepage, just like what happens when I used 'signin-oidc'. Why is that and how do I make it stop going there?
2. How can I have the Reply URL be different depending on whether you sign IN or UP? I can't really use a sign-up policy for one and a separate sign-in policy for the other, because the reply URL is identical.
I am new to .Net Core and I am at a loss as to how to even debug this. Signing in with B2C seems to be an obscure process. Any insight would be greatly appreciated.
EDIT:
This is the custom account controller, which is not built into the Nuget package for AzureADB2C.
While the Nuget package provides an internal AccountController, it does not allow you to set a custom Reply URL. Yet using my own account controller does not work for me. I am also not getting the "Correlation failed" error either, instead I get no error at all.
[Route( "[controller]/[action]" )]
public class AccountController : Controller
{
private readonly AzureADB2COptions azureAdB2COptions;
private const string PolicyAuthenticationProperty = "Policy";
private string scheme = AzureADB2CDefaults.AuthenticationScheme;
public AccountController( IOptions<AzureADB2COptions> b2cOptions )
{
azureAdB2COptions = b2cOptions.Value;
}
[HttpGet]
[Route( "/[controller]/SignIn" )]
public IActionResult SignIn()
{
var callbackUrl = Url.Action( nameof( OnboardingController.Start ), "Onboarding", values: null, protocol: Request.Scheme );
var properties = new AuthenticationProperties { RedirectUri = callbackUrl };
properties.Items[PolicyAuthenticationProperty] = azureAdB2COptions.SignUpSignInPolicyId;
return this.Challenge( properties, scheme );
}
The startup.cs code for B2C is unchanged from what the default .NETCore 2.1 template generated for me:
services.AddAuthentication( AzureADB2CDefaults.AuthenticationScheme )
.AddAzureADB2C( options =>
{
Configuration.Bind( "AzureAdB2C", options );
} );
An authentication request that is passed from your web application to Azure AD B2C can contain two redirect URLs:
One (often known as the reply URL) that is passed in the "redirect_uri" parameter, which must be registered with Azure AD B2C, to which all authentication responses are returned from Azure AD B2C to your web application. The default for this is /signin-oidc.
Another (often known as the return URL) that is round-tripped in the "state" parameter, which doesn't have to be registered with Azure AD B2C, to which the end user is returned after your web application has handled the authentication response. An example of this is /Onboarding/Start.
Your web application can set the return URL as follows:
public class AccountController : Controller
{
public IActionResult SignUp()
{
return this.Challenge(
new AuthenticationProperties()
{
RedirectUri = Url.Action("Start", "Onboarding", values: null, protocol: Request.Scheme)
},
AzureADB2CDefaults.AuthenticationScheme);
}
}
The ChallengeResult object creates an authentication challenge for the Azure AD B2C authentication middleware that is added by the AzureADB2CAuthenticationBuilderExtensions.AddAzureADB2C method.
The first argument to the ChallengeResult constructor invokes the OpenID Connect authentication handler that is registered by the Azure AD B2C authentication middleware.
The second argument to this constructor sets the return URL to which the end user will be returned after the Azure AD B2C authentication middleware has handled the authentication response.
It is my understanding that you will need to enable the the Application Claim -> newUser.
This flag is only set to true when a user initially signs up. Once they are redirected back to your website, you will need to read the claim and redirect to your onboarding/start or other controller if they are an existing user.
You can change the redirect_uri to any action in your website, but the problem is that the uri is only used as an endpoint. The action at the uri is never executed. So I think it will be very hard, if not impossible, to change the routing at the redirect_uri.
But I found another solution to make sure your Onboarding Registration is executed, using the Authorize attribute with a policy like: [Authorize(Policy="HasUserId")]
Take a look at https://stackoverflow.com/a/57672145
But I am never redirected to the intendend /Start page. instead I land back on the root homepage, just like what happens when I used
'signin-oidc'. Why is that and how do I make it stop going there?
How can I have the Reply URL be different depending on whether you sign IN or UP? I can't really use a sign-up policy for one and a
separate sign-in policy for the other, because the reply URL is
identical.
I was facing very similar issue to yours. You can have a look at this answer
which says
"The CallbackPath is the path where server will redirect during authentication. It's automatically handled by the OIDC middleware itself, that means we can't control the logic by creating a new controller/action and set CallbackPath to it . Below is the general process :
During authentication, the whole process is controlled by OpenID Connect middleware , after user validate credential in Azure's login page ,Azure Ad will redirect user back to your application's redirect url which is set in OIDC's configuration , so that you can get the authorization code(if using code flow) and complete the authentication process . After authentication , user will then be redirected to the redirect URL ."
Here you can find an example how I managed to handle redirects to different routes after login via AzureAd.
I am using this library https://github.com/openiddict/openiddict-core and implemented the oAuth token flow.
// Register the OpenIddict services.
// Note: use the generic overload if you need
// to replace the default OpenIddict entities.
services.AddOpenIddict(options =>
{
// Register the Entity Framework stores.
options.AddEntityFrameworkCoreStores<ApplicationDbContext>();
// Register the ASP.NET Core MVC binder used by OpenIddict.
// Note: if you don't call this method, you won't be able to
// bind OpenIdConnectRequest or OpenIdConnectResponse parameters.
options.AddMvcBinders();
// Enable the token endpoint (required to use the password flow).
options.EnableTokenEndpoint("/connect/token");
// Allow client applications to use the grant_type=password flow.
options.AllowPasswordFlow();
// During development, you can disable the HTTPS requirement.
options.DisableHttpsRequirement();
});
Mostly everything is working. I also included RoleManager along with it and roles are being stored in token as well using this:
var principal = await _signInManager.CreateUserPrincipalAsync(user);
foreach (var claim in principal.Claims.Where(c => c.Type == OpenIdConnectConstants.Claims.Role))
{
claim.SetDestinations(OpenIdConnectConstants.Destinations.AccessToken, OpenIdConnectConstants.Destinations.IdentityToken);
}
So far everything is done. After that admin user goes and modified the role to make him Supervisor etc. At this point of role change I want to make the previous tokens invalid. Is this possible in the library because at the moment he is able to use old token which has "Maid" role and is incorrect because somebody just changed his role to "Supervisor". In this case I want that when client sends old token it should not be treated as valid and should be sent back to login page.
How do I handle this in OpenIDDict library?
I'm building an application using backbone.js and web api. JS client will send ajax requests to access api. Building an API is pretty easy but i want to implement authentication and authorization for API.
I'm planning to return a token after a successful authentication and use this token for further requests. This token will be passed in HTTP Authorization headers.
My requirements are as below
1) Verify token on each request and get user id.
2) Use fetched user id for further actions.
First bit can be handled using Custom action filter where the permanent token can be verified against the database.
But i'm not able to find any sample or example for doing a second bit. I want to get a userid from a passed token and carry it further for later processing.
Is there any way of doing it?
Waiting for suggestions or ideas. Any code sample will really help. Thanks in advance.
You can set Thread.CurrentPrincipal upon successful token verification like this:
IPrincipal principal = new GenericPrincipal(new GenericIdentity(username), null);
Thread.CurrentPrincipal = principal;
// if we're running in IIS...
if ( HttpContext.Current != null )
HttpContext.Current.User = principal;
The principal might also be an instance of a custom class implementing the System.Security.Principal.IPrincipal interface (in order to be able to have its user ID associated).
I further suggest you use a DelegatingHandler instead of an action filter for the token verification in order to set the current principal as early as possible during the message lifecycle. Additionally, this way you don't have to decorate every action method/controller with the action filter attribute.
I highly recommend to use OAuth. Anyway, you can set the token and user info in session and use them in subsequent calls. i.e. if the session is active and user info exists then use them otherwise authorize and authenticate user (probably through OAuth) and if it is valid then store them in session to be used in subsequent calls.