I would like to Authenticate in App1 and then click a link to App2 and not have to authenticate. I figure a very common scenario. And this doc says it is super easy to achieve. So I created a Identity Authenticating app verbatim of the instructions here.
I applied the 2 extra lines of code as suggested in the doc
services.AddDataProtection()
.PersistKeysToFileSystem("{PATH TO COMMON KEY RING FOLDER}")
.SetApplicationName("SharedCookieApp");
services.ConfigureApplicationCookie(options => {
options.Cookie.Name = ".AspNet.SharedCookie";
});
I then and deployed it to my local IIS server(https://localhost/App1) and it was working perfectly.
For App2, I applied the same two lines of code as above and deployed it to my local IIS server(https://localhost/App2). After authenticating to App1 and then clicking the link to App2, i got an error "No authenticationScheme was specified, and there was no DefaultChallengeScheme found". I figured that had to be missing something in App2, but the doc only ever sites those two lines of code. Anyhow, I tried adding each of the following(not all at the same time) 4 options to make it happy and nothing worked.
services.AddAuthentication("Identity.Application");
services.AddAuthentication();//fails asking for default schema and challenge
services.AddDefaultIdentity<IdentityUser>().AddEntityFrameworkStores<SharedAuthContext>();//fails
services.AddAuthentication(options =>
{
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme;
});
Something is obviously missing from App2, but i cannot figure it out?
CookieAuthenticationDefaults.AuthenticationScheme is a constant string with value "Cookies".
Identity framework uses different names for its cookies.
IdentityConstants.ApplicationScheme -> "Identity.Application"
IdentityConstants.ExternalScheme -> "Identity.External" (we're not interested in this)
So you should change the names to
services.AddAuthentication(options => {
options.DefaultScheme = "Identity.Application";
options.DefaultChallengeScheme = "Identity.Application";
}).AddCookie("Identity.Application", options => { ... });
Also refer to Microsoft docs on sharing cookies between apps.
https://learn.microsoft.com/en-us/aspnet/core/security/cookie-sharing?view=aspnetcore-5.0
Related
Well, I'm really lost here so any help would be great. My app works with a DOTNET6 API backend and a Vue3 frontend.
I'm registering users via Google Sign In directly from my frontend (Vue3) with this code:
async googleLogIn() {
const provider = new GoogleAuthProvider;
var gUser;
await signInWithPopup(getAuth(), provider)
.then((result) => {
gUser = result.user;
console.log(gUser);
})
.catch((error) => {
console.log(error);
});
}
The user gets correctly saved in Firebase, and that should be all. The thing is, even though I'm not interacting with my DOTNET API, said API gets shut down without specifying the error. The message displayed in VS Debug Console is : ...\my_api.exe (process 32400) exited with code -1.
I believe the ports used by my API might be the problem (already tried changing them but it keeps failing), but I don't understand why the Google Sign In would interfere with my local API.
I am trying to set up Strava authentication (which is plain oAuth2) in Asp.Net Core Blazor App.
I am rather new to Blazor & Web dev (more of a backend background), and I don't seem to find out how to troubleshoot the reason why the Authentication does not work.
When I click the oAuth login button on the Login page in the (default) Blazor Server App, I get redirected to the correct oAuth login screen (of Strava in my case), but after I successfully enter the credentials for that App, the login page shows an error Error loading external login information.
While I would obviously appreciate any help or tips that could point out what is wrong in my code, I'm mostly searching for a way to get better error information and troubleshooting capabilities here. Setting a breakpoint in the EventHandler delegates does not show much.
This is the Startup.cs extract where I have configured the authentication setup :
services.AddAuthentication().AddOAuth("Strava",
oAuthOptions =>
{
oAuthOptions.ClientId = "myappid";
oAuthOptions.ClientSecret = "myclientsecret";
oAuthOptions.Scope.Clear();
oAuthOptions.Scope.Add("read");
oAuthOptions.CallbackPath = "/profile";
oAuthOptions.AuthorizationEndpoint = "https://www.strava.com/oauth/authorize";
oAuthOptions.TokenEndpoint = "https://www.strava.com/api/v3/oauth/token";
oAuthOptions.SignInScheme = IdentityConstants.ExternalScheme;
oAuthOptions.Events = new OAuthEvents()
{
OnRemoteFailure = loginFailureHandler =>
{
Console.WriteLine("Remote Error");
Console.WriteLine(loginFailureHandler.Failure.Message);
return Task.FromResult(0);
},
OnAccessDenied = handler =>
{
Console.WriteLine(handler.Response.StatusCode);
return Task.FromResult(0);
}
};
});
An update that made things work for me, so maybe it can help other people.
I performed the following actions, in order to gain more control on the entire authentication process.
I scaffolded two pages, in which I then could debug & step through (and obviously also update and change things). More information was found in this post:
Account.Login, which enables the customization of the actual login page dotnet aspnet-codegenerator identity -dc CotacolApp.Data.ApplicationDbContext --files "Account.Login"
Account.ExternalLogin, which enables the customization of the actual strava page dotnet aspnet-codegenerator identity -dc CotacolApp.Data.ApplicationDbContext --files "Account.ExternalLogin"
I then found out that the var info = await _signInManager.GetExternalLoginInfoAsync(); always resulted in a null value. And that was because I had to set the IdentityScheme to external. ```
And after that, I had to run some custom logic to do the claim mapping. Most of those details were written down in this stackoverflow post by #Morgeh.
Hope this can help people in the future.
I am trying to authenticate an app with Azure AD. It's all good in localhost, it redirects to Azure AD where I enter details to authenticate, and it sends back the token that allows to view the resource.
Everything managed behind the scenes with the Microsoft.AspNetCore.Authentication.AzureAD.UI 3.1.10 in an aspnetcore 3.1 application.
My app runs on http://localhost:5000 and I can configure the redirectUri/replyUri at Azure AD for that application to support this url. All good.
The problem is in a different environment when my app runs in a service fabric cluster.
I can see the problem
AADSTS50011: The reply URL specified in the request does not match the reply URLs configured for the application
When I inspect the url I can see that the redirect_uri has some url like this http://12.12.12.12/signin-oidc
The problem is double here. First of all I don't know which IP the cluster is gonna assign. Second, it is http, not https, and that's not supported by Azure AD.
Luckily my app has an external Url with a reverse proxy I can use to access. Something like https://myservicefabriccluster.com/MyApp
That Url I could configure as my redirect_uri in both my application and Azure AD, but I don't know how to do so.
My code has something like this:
services
.AddAuthentication(AzureADDefaults.AuthenticationScheme)
.AddAzureAD(options => Configuration.Bind("AzureAd", options));
where I bind my settings.
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"ClientId": "76245c66-354e-4a94-b34d-...",
"TenantId": "59c56bd4-ce18-466a-b515-..."
},
I can see the AzureADOptions supports some other parameters such as Domain (not needed) or CallbackPath (which by default is ok being /signin-oidc) but there is nothing similar to ReplyUrl or RedirectUri where I can specify an absolute URL as the callback.
I have found a few similar issues without an answer. Others suggest some kind of tricks like a middleware that rewrites that parameter just before redirecting to Azure AD.
Certainly there must be an easier way to deal with this problem that I expect is not so strange. Any help please?
The solution to overwrite redirect_uri parameter with a custom value is to use the Events available in OpenIdConnect library. This library should be available as it's a dependency for Microsoft.AspNetCore.Authentication.AzureAD.UI, so this is my solution that, in addition to the standard properties for AzureADOptions it adds a flag to determine whether the redirect uri must be overwritten and a value to do so. I hope it's self explanatory
services
.AddAuthentication(AzureADDefaults.AuthenticationScheme)
.AddAzureAD(options => configuration.Bind("AzureAd", options));
var isCustomRedirectUriRequired = configuration.GetValue<bool>("AzureAd:IsCustomRedirectUriRequired");
if (isCustomRedirectUriRequired)
{
services
.Configure<OpenIdConnectOptions>(
AzureADDefaults.OpenIdScheme,
options =>
{
options.Events =
new OpenIdConnectEvents
{
OnRedirectToIdentityProvider = async ctx =>
{
ctx.ProtocolMessage.RedirectUri =
configuration.GetValue<string>("AzureAd:CustomRedirectUri");
await Task.Yield();
}
};
});
}
services
.AddAuthorization(
options =>
{
options.AddPolicy(
PolicyConstants.DashboardPolicy,
builder =>
{
builder
.AddAuthenticationSchemes(AzureADDefaults.AuthenticationScheme)
.RequireAuthenticatedUser();
});
});
And the appsettings.json would have something like this:
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"ClientId": "76245c66-354e-4a94-b34d-...",
"TenantId": "59c56bd4-ce18-466a-b515-..."
"IsCustomRedirectUriRequired": true,
"CustomRedirectUri": "https://platform-cluster-development01.cubictelecom.com:19008/Scheduler/WebApi/signin-oidc"
},
Notice the IsCustomRedirectUriRequired and CustomRedirectUri are my custom properties that I read explicitly in order to overwrite (or not) the redirect uri query parameter when being redirected to the identity provider (i.e: Azure AD)
Looking at this, you should be configuring the public URL as the redirect URI, which is a value such as this:
https://myservicefabriccluster.com/MyApp
It looks like that the above library does not easily support this, and forces the redirect URI to be based on the HTTP listening URL of the code. As part of resolving this it is worth considering how you are writing your code:
This line of code indicates that your app is limited to only ever working with Azure AD:
- services.AddAzureAD
This line of code would ensure that your code works with both AzureAD and any other Authorization Server that meets the Open Id Connect standard:
- services.AddOpenIdConnect
The latter option also has an Events class with a commonly used OnRedirectToIdentityProvider operation that you can use to override the CallbackPath and provide a full RedirectUri.
Azure AD endpoints are standards based so you do not strictly have to use AzureAD specific libraries. Out of interest, I have a Single Page App Azure code sample that uses a neutral library like this, so I know this technique works.
I am developing now a Microservices .NET Core project. We have an Azure AD and we want to validate the requests that come to all web Apis microservice.
There are many apps who will call our Web API projects. Each one of these app has his own application (ClientId and client secret). They generate the token using their data and then send it in the header with every request. (No problem till now)
On my side, I have to validate the token with every request, but the validation should work for all the tokes that come from different applications.
So, I am trying to set the JWTBarear to accept any token, but I am a bit lost with the configuration:
services.AddAuthentication(o =>
{
o.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
o.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(jwt =>
{
jwt.Authority = "https://login.microsoftonline.com/[myTenant]";
});
services.AddAuthorization(auth =>
{
auth.AddPolicy("Bearer", new AuthorizationPolicyBuilder(JwtBearerDefaults.AuthenticationScheme)
.RequireAuthenticatedUser()
.Build());
});
and in the Configure:
app.UseAuthentication();
app.UseMvc();
But, I am getting always and 401. I generated the token using postman and it is valid.
I am not very good with the configurations. Am I missing something?
All the examples that I saw on the internet are using ClientId which I have many, not just one.
P.S. I don't have to sign in or something, I have to only validate the token.
Thanks!
Ooh, I found the answer. I was missing the Audience, which was different in .NET Core:
.AddJwtBearer(jwtOptions =>
{
jwtOptions.Audience = "https://management.core.windows.net";
jwtOptions.Authority = "https://login.microsoftonline.com/[myTenant]";
});
I have a .Net Core 2.0 application that I have added an API to. I'm using JWT to Authenticate requests and it is working nicely.
After going back to the MVC portion of my application, the login process seems to be no longer working. _signInManager.PasswordSignInAsync(...) works, but SignInManager.IsSignedIn(User) (from the view) is FALSE.
This is the result from _signInManager.PasswordSignInAsync(...):
? result
{Succeeded}
IsLockedOut: false
IsNotAllowed: false
RequiresTwoFactor: false
Succeeded: true
The User and claims seem to not be set properly. Here is how they look right after Sign in (breakpoint in view):
? User.Identities.First()
{System.Security.Claims.ClaimsIdentity}
Actor: null
AuthenticationType: null
BootstrapContext: null
Claims: Count = 0
CustomSerializationData: null
IsAuthenticated: false
Label: null
Name: null
NameClaimType: "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name"
RoleClaimType: "http://schemas.microsoft.com/ws/2008/06/identity/claims/role"
I tried with a different (new) user, but same results.
Also if I try to connect to a secure page I get a 401, but not redirected to the Login page.
Did I break my routine somehow? hmmm not sure where to look.
Update: This may be my problem...
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = "Jwt";
options.DefaultChallengeScheme = "Jwt";
}).AddJwtBearer("Jwt", options => ...
I think this is forcing my non-api controllers to look for a JWT Token which has not been provided. I just need to figure out how to do both COOKIE and JWT in the same app.
Tried adding this to my api controller:
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme) ]
When I add that, I get an Internal server error back on API requests and so does this line:
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme + "," + CookieAuthenticationDefaults.AuthenticationScheme)]
I don't have JWT and with Cookie Authentication completely working, but I have moved past this particular problem... so I'll go ahead and answer this specific question, then open another question for for the next issue.
The short answer is to comment out the lines listed in ConfigureServices.
services.AddAuthentication(options =>
{
// options.DefaultAuthenticateScheme = "Jwt";
//options.DefaultChallengeScheme = "Jwt";
})
If you set JWT as the default Auth Scheme, that breaks the cookie scheme in MVC. Once I commented out these lines Login and the rest of cookie authentication works fine.
I did still have some issues with JWT but I'll deal with that in another question.
I'm using this in API controllers to hook up JWT Auth.
[Route("API/[controller]/[action]")]
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]