I need help. Program WebApplication Code C# Asp.Net
First ,I've implemented a way to put a URI in Google OAuth 2 authorization.
Download the Json file for the program to read the file and generate the token.
Error
Failed to launch browser with "https://accounts.google.com/o/oauth2/v2/auth?access_type=offline&response_type=code&client_id=879100301665-3hpkd1h812d5eejji8o4mku3c2ci3rcs.apps.googleusercontent.com&redirect_uri=http%3A%2F%2F127.0.0.1%3A63526%2Fauthorize%2F&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fgmail.send" for authorization. See inner exception for details.
Second , I used to put values ββin Programs by assigning Client ID and Client secret in Google OAuth 2 authorization.
Error
Access to the path 'C:\Windows\system32\config\systemprofile' is denied
There are a number of issues with what you are doing.
First being that you have created what appears to be a web application credentials on Google cloud console. However the code you are using GoogleWebAuthorizationBroker.AuthorizeAsync is intended for use with installed applications only. This is why you are getting the error you are getting.
The code for creating an asp .net core web app is as follows
public void ConfigureServices(IServiceCollection services)
{
...
// This configures Google.Apis.Auth.AspNetCore3 for use in this app.
services
.AddAuthentication(o =>
{
// This forces challenge results to be handled by Google OpenID Handler, so there's no
// need to add an AccountController that emits challenges for Login.
o.DefaultChallengeScheme = GoogleOpenIdConnectDefaults.AuthenticationScheme;
// This forces forbid results to be handled by Google OpenID Handler, which checks if
// extra scopes are required and does automatic incremental auth.
o.DefaultForbidScheme = GoogleOpenIdConnectDefaults.AuthenticationScheme;
// Default scheme that will handle everything else.
// Once a user is authenticated, the OAuth2 token info is stored in cookies.
o.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
})
.AddCookie()
.AddGoogleOpenIdConnect(options =>
{
options.ClientId = {YOUR_CLIENT_ID};
options.ClientSecret = {YOUR_CLIENT_SECRET};
});
}
The second issue you are having is that the redirect uri you are adding is not a valid URL it needs to be https:// at the very least you are missing the //. As well as the fact that the client library will be sending the request using /signin-google
So a proper redirect uri should be more like this.
https://localhost:5001/signin-google
I have a YouTube video which shows How to get a Google users profile information, with C#. with a companion blog post Asp .net core 3 and Google login both should work fine with .net 5 as well.
Related
I want to manually authorize an app that runs in the console where there is no browser available.
Basically I need the same code as they are using on this website but the C# Equivalent.
I just can't seem to find the part in the C# Library that allows me to print a link to the console instead of opening a browser.
My current implementation I have just opens the browser and throws an Error if it cant.
GoogleClientSecrets? fromStream = await GoogleClientSecrets.FromStreamAsync( stream , taskCancellationToken );
UserCredential credential = await GoogleWebAuthorizationBroker.AuthorizeAsync( fromStream.Secrets
, scopes
, user
, taskCancellationToken
, new FileDataStore( nameof(Authorize) ) );
The GoogleWebAuthorizationBroker.AuthorizeAsync method is designed for installed applications. Installed applications will open the browser on the machine the code is running on.
The following example is designed for web applications. The application would need to be running on a web server.
public void ConfigureServices(IServiceCollection services)
{
...
// This configures Google.Apis.Auth.AspNetCore3 for use in this app.
services
.AddAuthentication(o =>
{
// This forces challenge results to be handled by Google OpenID Handler, so there's no
// need to add an AccountController that emits challenges for Login.
o.DefaultChallengeScheme = GoogleOpenIdConnectDefaults.AuthenticationScheme;
// This forces forbid results to be handled by Google OpenID Handler, which checks if
// extra scopes are required and does automatic incremental auth.
o.DefaultForbidScheme = GoogleOpenIdConnectDefaults.AuthenticationScheme;
// Default scheme that will handle everything else.
// Once a user is authenticated, the OAuth2 token info is stored in cookies.
o.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
})
.AddCookie()
.AddGoogleOpenIdConnect(options =>
{
options.ClientId = {YOUR_CLIENT_ID};
options.ClientSecret = {YOUR_CLIENT_SECRET};
});
}
These are your two options unless you are able to use service account authorization. Service account authorization is intended for server to server communication between your application and an account you the developer control. It does not work with all google apis.
options
Consider running your code once locally, then copy credentials file that is created by FileDataStore onto the server along with your code. Google .net β FileDatastore demystified
print link
There used to be a method to print the authorization link but i will have to dig around in the library for it tomorrow.
I have one API and one Client app (Blazor web assembly) separately.
The client app authenticates users through the Azure active directory. In the client when I have saved any file through API, I am using static files configured on Startup.cs such as :
app.UseStaticFiles(new StaticFileOptions()
{
FileProvider = new PhysicalFileProvider(Path.Combine(#"c:\", #"Resources")),
RequestPath = new PathString("/secureFile"),
});
The problem is the client application is working fine. But if someone inspects the Html and gets the image source they can directly access the files through the browser like https://localhost:44397/secureFile/profile.jpg
On the other hand, because I am using Azure active directory login, the API does not save any user information. That's why I can't use the scenario like:
app.UseAuthentication();
app.UseAuthorization();
app.UseStaticFiles(new StaticFileOptions()
{
FileProvider = new PhysicalFileProvider(Path.Combine(#"c:\", #"Resources")),
RequestPath = new PathString("/secureFile"),
OnPrepareResponse = ctx =>
{
if (!ctx.Context.User.Identity.IsAuthenticated)
{
// respond HTTP 401 Unauthorized.
ctx.Context.Response.StatusCode = (int) HttpStatusCode.Unauthorized;
ctx.Context.Response.ContentLength = 0;
ctx.Context.Response.Body = Stream.Null;
// ctx.Context.Response.Redirect("/")
}
}
});
Here the !ctx.Context.User.Identity.IsAuthenticated is always false even the client application is calling.
May Client application is Authenticating though :
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.Audience = Configuration["AAD:ResourceId"];
options.Authority = $"{Configuration["AAD:InstanceId"]}{Configuration["AAD:TenantId"]}";
});
My goal is to give access to the https://localhost:44397/secureFile/profile.jpg if the client application is calling the image source. But should block if anyone calls through the URL directly.
Any lead will help me a lot.
I have already followed article but no use in my scenario because I am using AAD login.
You mention that "the API does not save any user information," but regardless of the authentication flow, some authentication information has to be established even transiently for the individual request so that authorization can be evaluated.
In the example code you have listed, you're invoking a check on the HttpContext User claims principal to see if the user is authenticated, and you mention it's always coming back as false, even if it's coming from your client application. What's notable here is that something has to invoke the authentication flow in an application before authorization can be performed. Because nothing in the StaticFileMiddleware inherently does this, it probably never is happening during that request.
You can invoke that authentication flow yourself through the use of the ChallengeAsync() method, which is intended to "challenge the current request using the default challenge scheme. An authentication challenge can be issued when an unauthenticated user requests an endpoint that requires authentication":
https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.authentication.authenticationhttpcontextextensions.challengeasync?view=aspnetcore-6.0
You may need to customize the behavior for your particular case, especially if you have to accommodate multiple schemes, but a general starting point would be adding a using Microsoft.AspNetCore.Authentication; to ensure the HttpContext extensions are available, then adding a corresponding call to await ctx.Context.ChallengeAsync() within your flow if the user is not authenticated.
Once that runs, you can check again whether the user is both authenticated and whether they meet the authorization requirements you would like to have for your secure static files.
Security is one of those things that once you set it up, you kinda forget about it, until something doesn't work. I have a .Net Core application that uses OpenId Connect with Azure. The sign in works fine, but we noticed a few days ago that the signout wasn't working. You could sign out of the application, but then login again directly without entering credentials. So, I have been looking around and found out that it's not good enough to clear the cookies and session, you need to go to the "end_session_endpoint" to actually clear the credentials. I have looked in a number of places, but I can't find a simple code example of how to to this. And the code examples I have tried don't seem to work. When I run locally, I can logout and it says it logged me out, but when I start the application again, I am logged in automatically right away. I understand the concept, I just don't know how to do it. Below is what my authentication looks like in the Startup.cs file:
services.AddAuthentication(options =>
{
})
.AddCookie()
.AddOpenIdConnect(options =>
{
options.Authority = Configuration["Authentication:Microsoft:OAuth"];
options.RequireHttpsMetadata = true;
options.ResponseType = OpenIdConnectResponseType.CodeIdToken;
options.UsePkce = false;
options.Scope.Clear();
options.Scope.Add("openid");
options.Scope.Add("email");
options.SaveTokens = true;
options.CallbackPath = new PathString(Configuration["Authentication:Microsoft:Callback"]);
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = false,
};
// MetadataAddress represents the Active Directory instance used to authenticate users.
options.MetadataAddress = Configuration["Authentication:Microsoft:Meta"];
options.ClientId = Configuration["Authentication:Microsoft:ApplicationId"];
options.ClientSecret = Configuration["Authentication:Microsoft:Password"];
});
Does anyone have a straight forward code example for this?
RP INITIATED LOGOUT
This is mostly a case of sending the standard message with these parameters.If I remember rightly, Azure may require the third of these to log you out successfully.
client_id
post_logout_redirect_uri
id_token_hint
EXAMPLES
Here is a C# example that sends this type of request. It uses the older .Net framework, but should be easy to follow.
In .Net Core, you can set a SignedOutCallbackPath in the OIDC properties, which must match a post_logout_redirect_uri registered against the client in Azure AD. You can then call this code in the controller and it should send the standard mesaage.
await HttpContext.SignoutAsync():
Personally I find this .NET syntax and C# layer a little unintuitive, but ultimately it is just a wrapper around the standard messages.
My Azure SPA example does an RP initiated logout, and I can confirm that the above parameters work fine with Azure AD.
ADVANCED OPTIONS
Sometimes logout can be a very tricky area to get the best usability behaviour, depending on the provider. In case useful later, another option is to use the max-age parameter from OpenID Connect during sign in. You then get an auth_time claim in the ID token. On the next redirect after auth_time has expired you can use this data to send a prompt=login parameter if required, to force the user to re-authenticate. The .NET way to do this is described in this post.
UPDATE
Here is what my SPA config looks like, with the post logout redirect URI, to check against your own configuration. RP (relying party) is your C# web app, OP (openid connect provider) is Azure AD (I never liked this type of terminology). The logout mechanics are just focused on removing the SSO cookie that Azure AD sets. Azure AD logout behaviour may not be easy to control fully though, since logout in OAuth technologies can sometimes get complex and involve multiple apps.
TEST OPTION
One option you could try is logging in and out using OAuth Tools, which is useful for troubleshooting this type of problem without needing to write code. Once you get the correct behaviour here, you'll be clearer about what you need to code:
First find your OpenID Connect metadata URL - this is my URL
Next select Add Environment, give it a name like my and enter the metadata URL - then make sure this environment is selected
Add https://oauth.tools/callback/code to Azure AD as a redirect URI
Then select Code Flow in OAuth tools, enter your development Azure AD client ID and client secret, then select Run, to trigger a redirect
Then scroll down and click Redeem Authorization Code, which will get tokens, including an ID token which might be needed for logout
Then run a Logout Flow, for which you'll need to set https://oauth.tools in Azure AD as the front channel logout URL
If you can't get logout to work as you'd like, try the prompt=login option, which will force a new login
Q & A
So OIDC logout is working but a second factor time to live is not. This is outside the OIDC specs and probably cannot be influenced by code. Try it in a new Incognito browser window and see if you have to enter the second factor. If so then it will be a cookie that you may be able to identify if you trace the HTTP traffic. Look for a configuration setting somewhere that controls this - something similar to the Curity TTL setting in this article. If there is no such setting then it may be outside your control.
Leftover browser sessions. In some ways if users let other people use their desktop sessions then all bets are off. You can get too hung up on this. Just aim to be as good as other apps. Eg are you as secure as Office 365 itself, Gmail etc?
By default (without a post logout redirect URI) the user is dumped back in the Azure login screen, which is typically not what the user wants. Instead a post logout landing screen gives your app control over the UX. A common option is to just display something like You are signed out, click here to log back in. In a C# website this would just return a view to the browser.
Its very difficult to ask questions on ID4. Also all relevant discussions I could google point to non-existing links for code samples.
What I have: an angular client uses ID4 auth made up of MVC pages provided by Microsoft, hosted in ASP.NET Core.
Startup.cs
services.AddDefaultIdentity<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = true)
.AddEntityFrameworkStores<AppDbContext>();
services.AddIdentityServer()
.AddApiAuthorization<ApplicationUser, AppDbContext>();
services.AddAuthentication()
.AddIdentityServerJwt();
. . .
app.UseAuthentication();
app.UseIdentityServer();
app.UseAuthorization();
authorize.service.ts
const settings: any = await response.json();
settings.automaticSilentRenew = true;
settings.includeIdTokenInSilentRenew = true;
this.userManager = new UserManager(settings);
Use case: a user logs in on the webpage, ticks Remeber Me checkbox and uses the auth'ed website parts. If the server restarts - the tokens lost and user interaction with the website becomes broken - e.g. there are no errors in the webbrowser console and nothing happens if user tries to access any server-side data. It looks like nothing is there. Not as if there is issue with the auth.
It can only be fixed by manually logout/login. Or by opening new tab and going to the app again.
Is there simple explanation on to how can I a) detect that client token is broken and b) request new token?
EDIT if a user continues to use website next working day or opens new browser tab (without server restart) the ID4 works as expected - no login required.
The fiddler:
The short answer is: nothing should break if the server process (either application backend or identityserver4-based IDP) restarts so chances are you're missing some persistence (for persisted grants) and/or shared config (e.g. token signing keys and ASP.Net data protection keys) and things are being regenerated on startup and in-memory data is being lost.
This article covers the things you need to consider to deploy a viable production service: https://docs.identityserver.io/en/latest/topics/deployment.html
With that all in place though tokens will still expire and there are a couple of ways to refresh them depending on the context and grant type in use.
To detect if a token is not valid anymore:
Use it and detect if you get a 401 response from the endpoint you're calling
Check the exp claim inside the token yourself
Use the expires_in value returned with the token and calculate the expiry time based on that
To renew it (and some libs will automate this):
Use the iframe-based silent renewal mechanism (authorize endpoint with prompt=none) - note that third part cookie restrictions come into play for this
Use a refresh token via the token endpoint (not recommended for client side apps due to the need to persist a refresh token in the client side)
I have a project that hosts the IdentityServer4 and I am attempting to also host in the same project a Web API, which accepts the access-token.
My question is, is possible that a single project contains the IdentityServer and an Web API that consume the same IdentityServer?
EDIT: The API must be secured with the Authorize attribute
I have an identity server 4 project, in the same project there is an API for CIUD of the clients. (Lets call it developer console api).
I then have a side project with is an asp .net core project that contains the actual razor pages for the Developer console it access the API within the Identity server project.
The reason i did it this way is that only one project should be updateing the database. So to update the database owned by the identity server it was decided the the API for accessing it should also be within the same project.
Yes you can have a web api from within your Identity server 4 project.
Configure service
services.AddAuthentication(IdentityServerConstants.DefaultCookieAuthenticationScheme)
.AddIdentityServerAuthentication(options =>
{
// base-address of your identityserver
options.Authority = settingsSetup.Settings.Authority;
// name of the API resource
options.ApiName = "testapi";
options.RequireHttpsMetadata = false;
});
Configure
I think it needs to have both of these.
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
app.UseAuthentication();
app.UseIdentityServer();
Endpoints
Because the requests are sent using the access token as a bearer token then the authorize for each of the API calls needs to include the authencationScheme. I havent exactly figured out why but without this it doesnt work.
[HttpGet("Client/List")]
[Authorize(AuthenticationSchemes = "Bearer")]
public ActionResult ClientList()
{
}
While #DaImTo's answer is correct and working and it's developed by IdentityServer team, it uses Introspection Endpoint which means for every request AddIdentityServerAuthentication will create a http request and send it to your server, which is the same app.
I developed a library called IdentityServer4.Contrib.LocalAccessTokenValidation which do the exact same thing but without using Introspection Endpoint. It will authenticate the token directly from TokenStore which is configured in Services. You can use it if you are interested.
nuget link : https://www.nuget.org/packages/IdentityServer4.Contrib.LocalAccessTokenValidation
github link : https://github.com/Kahbazi/IdentityServer4.Contrib.LocalAccessTokenValidation