How can I invoke callback while login in Google in .net core? - asp.net-core

I am coding a third-party login-in with Google.
Here is the tutorial of Microsoft:
https://learn.microsoft.com/en-us/aspnet/core/security/authentication/social/google-logins?view=aspnetcore-3.0
I did what it said by using the Secret Manager and AddAuthentication
When I click the Login button on the website, it will redirect to google login successfully.
Now the next step is the invoke the callback. However, I don't know how to invoke it after login.
The tutorial of Microsoft only shows these two class:
https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.authentication.google.googleoptions?view=aspnetcore-2.2
https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.authentication.remoteauthenticationoptions.callbackpath?view=aspnetcore-2.2
But does not say something more about that yet. I even don't know how to use it and I can't find any other tutorial about it.
Would you please tell me how can I invoke the callback? Thank you.

You can use like below. Login user on External login method then get the result with confirmlogin method.
[HttpGet]
public async Task<object> ExternalLogin()
{
string redirectUrl = "api/account/ConfirmLogin&Provider=Google";
await AuthenticationProperties properties =
SignInManager.ConfigureExternalAuthenticationProperties("Google", redirectUrl);
return new ChallengeResult(provider, properties);
}
[HttpGet]
public async Task ConfirmLoginAsync(string Provider)
{
/*
make your checks
*/
Request.HttpContext.Response.Redirect(url);
}
Also you need to register your url like :
registerUrl
I hope it helps.

Related

Blazor WASM standalone project reauthentication for multiple browser tabs after logout

I have implemented authorization with JWT auth on our blazor WASM project. All was well - logging in and logging out...until
Scenario:
I noticed that if I duplicated my 'logged in (authenticated identity user) tab', now having two valid authorized user tabs open. In the first tab I log out: I am re-directed to the login page, the stored token is gone, everything seems ok - but when I go to the second tab and click to go to a new page(within the tab) I am able to do so. When I debug and check the auth state.. it still has the validated identity user!
Expected Results:
When I logout in the first autenticated tab, I expect the auth state of the second tab to also be deauthenticated.
Error Messages:
Currently none
What I have tried:
I searched around for someone solving the same thing for Blazor WASM standalone apps. I did come across this guy's video where he describes most people are using the AuthenticationStateProvider wrong by injecting it directly instead of using a cascading parameter. He actually then demonstates the exact issue I am having - but then proceeds to solve it with a Balzor Server class library(using RevalidateServerAuthenticationStateProvider with some custom code on the ValidateAuthenticationStateAsync function)! Not a WASM solution! Link to his video: https://www.youtube.com/watch?v=42O7rECc87o&list=WL&index=37&t=952s - 13:23 he demonstrates the issue I have attempted to depict.
Code of my logout sequence:
To start - when you click the logout button - I redirect to a logout page. We will start here:
Logout.Razor.cs
[CascadingParameter]
public Task<AuthenticationState> AuthenticationStateTask { get; set; }
protected override async Task OnInitializedAsync()
{
await AuthService.Logout();
var authState = await AuthenticationStateTask;
var user = authState.User;
if (!user.Identity.IsAuthenticated)
{
NavManager.NavigateTo("/");
}
}
You can see you then are waiting for the AuthService to log you out.
AuthService.cs
private readonly AuthenticationStateProvider _authStateProvider;
public async Task Logout()
{
await _localStorage.RemoveItemAsync("authToken");
await ((CustomAuthStateProvider)_authStateProvider).NotifyUserLogout();
_client.DefaultRequestHeaders.Authorization = null;
}
Then you are waiting on the CustomAuthStateProvider:AuthenticationStateProdiver to notify that the state has changed.
CustomAuthStateProvider.cs
_anonymous = new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity()));
public async Task NotifyUserLogout()
{
var authState = Task.FromResult(_anonymous);
NotifyAuthenticationStateChanged(authState);
}
At this point in the active page we are logged out and redirected to the login page.
I was hoping there is some browser session option for the authstate I am missing or there is some microsoft documentation of handling this situation on WASM projects - but my peanut brain can't seem to find it.
Additional Information: I am purely only using the AuthenticationStateProdiver for a custom login.
I am not using OidcAuthentication or MsalAuthentication services.
This is a standlone WASM app with a completely decoupled API. All .net6 living on azure.
Looking forward to see if anyone else has this issue!
BR,
MP

how to perform "dry-run" authorization check in .NET Core?

Consider that I have .NET Controller with Policy-based authorization:
public class ImportantController: Controller {
[HttpGet]
[Authorize(Policy = "CanAccessVIPArea")]
public IActionResult ShowInformation() {
...
return OK(VipData);
}
[HttpPost]
[Authorize(Policy = "CanChangeVIPData")]
public IActionResult SaveInformation([FromBody] VipData) {
...
return CreatedAtAction(...);
}
}
Obviously, the real example is much more complex; I apologize if my simplification leads to too much make-believe in it. Also, real application is SPA with Angular front end; but I don't think it makes any difference for the purposes of this question.
When the user calls ShowInformation() I show a lot of data. On that page I have Save button that calls SaveInformation(). Authorization middleware checks for the right policy and it all works fine.
The problem is that by the time the user presses Save, she entered a lot of data, only to find out that she doesn't have the permissions to save. Obviously, leading to bad experience. I want to check for permissions on SaveInformation in the middleware that gets invoked when the user calls ShowInformation. I would prefer not to check for the hardcoded policy because it is on the server and it can change (we have pretty sophisticated permission management system that manipulates permissions at runtime). Invocation of SaveInformation is in the same Angular service as ShowInformation, and it is very easy to check...
I would like to invoke something like /api/SaveInformation?dryrun that will short-circuit the pipeline after authorization middleware with success or failure.
You can inject an IAuthorizationService and ask to evaluate a policy by name:
public class ImportantController: Controller
{
private readonly IAuthorizationService authorization;
public ImportantController(IAuthorizationService authorization)
{
this.authorization = authorization;
}
public async Task<IActionResult> ShowInformation()
{
// ...
var result = await authorizationService.AuthorizeAsync(User, "IsLucky");
return OK(VipData);
}
}
My pratice is to include all permission claims in the id token, when the user first login to the system, the id token will return to the client side. The client side then render the page according to the permission claims.

Need to handle Post Authenticate in Asp.Net Core

I'm ready to use Asp.Net core, but here's what I am doing. In MVC 5, I have an Http module that is handling the PostAuthenticate event in order to create the claim where I am doing some stuff to determine roles for the user. I see no way to do this same thing in Core. Note that this is using Windows Authentication so there is no login method to handle.
From the current httpModule that hooks up to the PostAuthenticate because I want to initialize some things for the user.
context.PostAuthenticateRequest += Context_PostAuthenticateRequest;
Note that httpModules no longer exist with Core and that is being moved to middleware.. I don't see how to tap into that event from there though.
I just did this for the first time today. Two basic steps here.
First:
Create a class that implements the IClaimsTransformer interface.
public class MyTransformer : IClaimsTransformer
{
public Task<ClaimsPrincipal> TransformAsync(ClaimsTransformationContext context )
{
//don't run if user isn't logged in
if(context.Principal.Identity.IsAuthenticated)
{
((ClaimsIdentity)context.Principal.Identity)?.AddClaims(...);
}
}
return Task.FromResult(context.Principal);
}
Second:
Add this line to Startup.cs in
public void Configure(IApplicationBuilder app, ..., ...)
{
//app.Use...Authentication stuff above, for example
app.UseOpenIdConnectAuthentication( new OpenIdOptions
{
//or however you like to do this.
});
app.UseClaimsTransformation(o => new MyTransformer().TransformAsync(o));
//UseMvc below
app.UseMvc(...);
}
Keep in mind that TransformAsync is going to run on every request, so you might want to look into using sessions or caching if you're hitting a database with it.
Windows Authentication is performed by the hosts (IIS or HttpSys/WebListener) at the start of your application pipeline. The first middleware in your pipeline is the equivalent of PostAuthenticateRequest in this case. Operate on HttpContext.User as you see fit.

AspNet Core External Authentication with Both Google and Facebook

I am trying to implement the Form-Authentication in ASP.Net Core with Both Google and Facebook Authentications. I followed some tutorials and after some struggles, I managed to make it work both.
However, the problem is that I cannot use both authentications for the same email.
For example, my email is 'ttcg#gmail.com'.
I used Facebook authentication to log in first... Registered my email and it worked successfully and put my record into 'dbo.ASPNetUsers' table.
Then I logged out, clicked on Google Authentication to log in. It authenticated successfully, but when I tried to register it keeps saying that my email is already taken.
I tried to do the same thing for other online websites (Eg, Stackoverflow). I used the same email for both Google and Facebook and the website knows, I am the same person and both my login / claims are linked even though they come from different places (Google & Facebook).
I would like to have that feature in my website and could you please let me know how could I achieve that.
In theory, it should put another line in 'dbo.AspNetUserLogins' and should link the same UserId with multiple logins.
Do I need to implement my own SignInManager.SignInAsync method to achieve that feature? Or am I missing any configuration?
You need to link your Facebook external login to your Google external login with your email by using UserManager.AddLoginAsync, you cannot register twice using the same adresse if you use the adresse as login.
Check out the Identity sample on Identity github repo.
https://github.com/aspnet/Identity/blob/dev/samples/IdentitySample.Mvc/Controllers/ManageController.cs
To link external login to a user, the Manae controller expose methods LinkLogin and LinkLoginCallback
LinkLogin requests a redirect to the external login provider to link a login for the current user
LinkLoginCallback processes the provider response
//
// POST: /Manage/LinkLogin
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult LinkLogin(string provider)
{
// Request a redirect to the external login provider to link a login for the current user
var redirectUrl = Url.Action("LinkLoginCallback", "Manage");
var properties = _signInManager.ConfigureExternalAuthenticationProperties(provider, redirectUrl, _userManager.GetUserId(User));
return Challenge(properties, provider);
}
//
// GET: /Manage/LinkLoginCallback
[HttpGet]
public async Task<ActionResult> LinkLoginCallback()
{
var user = await GetCurrentUserAsync();
if (user == null)
{
return View("Error");
}
var info = await _signInManager.GetExternalLoginInfoAsync(await _userManager.GetUserIdAsync(user));
if (info == null)
{
return RedirectToAction(nameof(ManageLogins), new { Message = ManageMessageId.Error });
}
var result = await _userManager.AddLoginAsync(user, info);
var message = result.Succeeded ? ManageMessageId.AddLoginSuccess : ManageMessageId.Error;
return RedirectToAction(nameof(ManageLogins), new { Message = message });
}

Getting Twitter Access Secret using DotNetOpenAuth in MVC4

I'm creating an app with MVC4 that will authorize users using Twitter and lets them tweet from the app as well. I'm able to get the user authenticated without a problem using the BuiltInOAuthClient.Twitter that is in MVC4. http://www.asp.net/web-pages/tutorials/security/enabling-login-from-external-sites-in-an-aspnet-web-pages-site
I have the access token, and oauth_verifier, but I need to get the acess_secret back from Twitter as well. https://dev.twitter.com/docs/auth/implementing-sign-twitter
What I'm missing is how to pass the oauth_verifier back to Twitter to get the access secret using OAuthWebSecurity.
Again, I can use Twitter for the login ok, but I need to be able to use twitter as the user as well. I've done this with the TweetSharp library before, but am trying to use DotNetOpenAuth on this project.
UPDATE:
I'm using the OAuthWebSecurity class as described in the first link to manage authentication. OAuthWebSecurity.RegisterClient in the AuthConfig expects a DotNetOpenAuth.AspNet.IAuthenticationClient. You can't swap that out with the TwitterConsumer class as suggested.
I can use the "built in" DotNetOpenAuth authentication piece as described in the first link, OR I can use custom code to do the full authorization, but I'm trying to find a way to do both.
I can do it separately, but then the user is presented with the Twitter dialog twice (once to login and once to authorize). I'm hoping there's a way to use the already wired up authentication piece that uses OAuthWebSecurity but ad the authorization piece as well.
I've been banging my head against a wall with this for a few days now, but I finally have something that works. Would be interested to know if it's a valid solution though!
First off, create a new OAuthClient:
public class TwitterClient : OAuthClient
{
/// <summary>
/// The description of Twitter's OAuth protocol URIs for use with their "Sign in with Twitter" feature.
/// </summary>
public static readonly ServiceProviderDescription TwitterServiceDescription = new ServiceProviderDescription
{
RequestTokenEndpoint =
new MessageReceivingEndpoint(
"https://api.twitter.com/oauth/request_token",
HttpDeliveryMethods.GetRequest | HttpDeliveryMethods.AuthorizationHeaderRequest),
UserAuthorizationEndpoint =
new MessageReceivingEndpoint(
"https://api.twitter.com/oauth/authenticate",
HttpDeliveryMethods.GetRequest | HttpDeliveryMethods.AuthorizationHeaderRequest),
AccessTokenEndpoint =
new MessageReceivingEndpoint(
"https://api.twitter.com/oauth/access_token",
HttpDeliveryMethods.GetRequest | HttpDeliveryMethods.AuthorizationHeaderRequest),
TamperProtectionElements = new ITamperProtectionChannelBindingElement[] { new HmacSha1SigningBindingElement() },
};
public TwitterClient(string consumerKey, string consumerSecret) :
base("twitter", TwitterServiceDescription, consumerKey, consumerSecret) { }
/// Check if authentication succeeded after user is redirected back from the service provider.
/// The response token returned from service provider authentication result.
protected override AuthenticationResult VerifyAuthenticationCore(AuthorizedTokenResponse response)
{
string accessToken = response.AccessToken;
string accessSecret = (response as ITokenSecretContainingMessage).TokenSecret;
string userId = response.ExtraData["user_id"];
string userName = response.ExtraData["screen_name"];
var extraData = new Dictionary<string, string>()
{
{"accesstoken", accessToken},
{"accesssecret", accessSecret}
};
return new AuthenticationResult(
isSuccessful: true,
provider: ProviderName,
providerUserId: userId,
userName: userName,
extraData: extraData);
}
}
The important part is where you cast the response to an ITokenSecretContainingMessage. It appears that the response has the TokenSecret all along, but it is only on an internal property. By casting it, you get access to a public property. I can't say that I'm a fan of doing this, but then I also don't understand why DotNetOpenAuth the Asp.Net team have hidden the property in the first place. There must be a good reason.
You then register this client in AuthConfig:
OAuthWebSecurity.RegisterClient( new TwitterClient(
consumerKey: "",
consumerSecret: ""), "Twitter", null);
Now, in the ExternalLoginCallback method on the AccountController, the accessSecret is available in the ExtraData dictionary.
The DotNetOpenAuth.AspNet.Clients.TwitterClient class only allows authentication, not authorization. So you wouldn't be able to post tweets as that user if you use that class.
Instead, you can use DotNetOpenAuth.ApplicationBlock.TwitterConsumer, which does not share this limitation and you can even copy the source code for this type into your application and extend it as necessary.
You should be able to enhance the TwitterConsumer class (once you've copied it into your own project) to implement the required interface so that the OAuthWebSecurity class will accept it. Otherwise, you can just use TwitterConsumer directly yourself to both authenticate and authorize your web app so the user only sees Twitter once but you get all the control you need. After all, folks using ASP.NET have been using TwitterConsumer to both login and authorize for subsequent calls to Twitter for long before OAuthWebSecurity even existed.
For a WebForms project template which references Microsoft.AspNet.Membership.OpenAuth in AuthConfig.cs instead of Microsoft.Web.WebPages.OAuth (MVC4 Internet Application) I was able to modify Paul Manzotti's answer to get it to work:
Create a custom twitter client class that derives from DotNetOpenAuth.AspNet.Clients.TwitterClient
public class CustomTwitterClient : TwitterClient
{
public CustomTwitterClient(string consumerKey, string consumerSecret) :
base(consumerKey, consumerSecret) { }
protected override AuthenticationResult VerifyAuthenticationCore(AuthorizedTokenResponse response)
{
//return base.VerifyAuthenticationCore(response);
string accessToken = response.AccessToken;
string accessSecret = (response as ITokenSecretContainingMessage).TokenSecret;
string userId = response.ExtraData["user_id"];
string userName = response.ExtraData["screen_name"];
var extraData = new Dictionary<string, string>()
{
{"accesstoken", accessToken},
{"accesssecret", accessSecret}
};
return new AuthenticationResult(
isSuccessful: true,
provider: ProviderName,
providerUserId: userId,
userName: userName,
extraData: extraData);
}
}
Add the custom client in AuthConfig.cs
public static void RegisterOpenAuth()
{
OpenAuth.AuthenticationClients.Add("Twitter", () => new CustomTwitterClient(
consumerKey: ConfigurationManager.AppSettings["twitterConsumerKey"],
consumerSecret: ConfigurationManager.AppSettings["twitterConsumerSecret"]));
}
Ta-dow! Now you can haz access secret.
You can extract the oauth_token_secret from OAuthWebSecurity by designing your own TokenManager. You can register the token manager when you register your Twitter client in OAuthWebSecurity.RegisterClient.
I used this method to extract the needed values to be able to bypass the authorization step of the Linq-to-Twitter lib.
I will soon post my solution at my blog.