How to integrate Social Login with existing .Net core Web API backend and Angular SPA frontend with working OpenIddict user/password and bearer token - asp.net-core

TL;DR
Question: how to implement social login (OAuth2 authorization flow) with an existing SPA/Web API application that is based on identity, user/password, bearer token authentication?
I have an existing application that has:
Backend: .Net Core 2 Web API with Identity and OpenIddict services configured, with a working authentication process based on user/password challenge for bearer token.
Users are stored with Identity (AspNetUsers).
Part of the Startup.cs code
// Register the OpenIddict services.
services.AddOpenIddict()
.AddCore(options =>
{
options.UseEntityFrameworkCore().UseDbContext<ApplicationDbContext>();
})
.AddServer(options =>
{
options.UseMvc();
options.EnableTokenEndpoint("/connect/token");
options.AllowPasswordFlow();
options.AllowRefreshTokenFlow();
options.AcceptAnonymousClients();
options.RegisterScopes(
OpenIdConnectConstants.Scopes.OpenId,
OpenIdConnectConstants.Scopes.Email,
OpenIdConnectConstants.Scopes.Phone,
OpenIdConnectConstants.Scopes.Profile,
OpenIdConnectConstants.Scopes.OfflineAccess,
OpenIddictConstants.Scopes.Roles);
})
.AddValidation();
.
Frontend: SPA Angular 7 app that consumes this backend API and token authorization
So basically the current setup is, user inputs user/password to the SPA that invokes the backend /connect/token endpoint that validates the credentials and generates the token for the client.
And now I need to integrate Social Login (OAuth2 Authorization flow) so that
user chooses login with provider,
gets redirected to providers authorization page,
gets redirected back to my application that
needs to create the Identity user and save the Identity UserLoginInfo data and
provide my application token so that the user can login.
I understand the OAuth2 authorization flow that needs to Request an Authorization Code and then Exchange Authorization Code for an Access Token for that provider. I also know that this flow must use backend, once it uses sensitive information like client_secret that can't be stored in client side.
But at some point user needs to interact with frontend, so connecting these parts seems very difficult considering that these are wide used technologies. All practical examples I found on Google were using .Net Core MVC application. I also found this article ASP.NET Core 3.0 Preview 4 - Authentication and authorization for SPAs that seems promising but is still in Preview 4.
I already created the social providers apps and I have client_id, client_secret. Also registered my redirects url's.
What I tried with no success was:
In frontend user chooses login with social provider,
User gets redirected to provider authorization page, authenticates himself and
gets redirected from the provider to my frontend URL (redirect_uri) with the provider's code then
my frontend calls my backend /connect/token existing endpoint passing the selected provider and the received code, the endpoint was programmed to receive the provider and code also, then
my backend calls provider's get AccessToken url posting "grant_type", "authorization_code" "code", code "redirect_uri", "https://same_frontend_host/same/path" "client_id", providerClientId "client_secret", providerSecret and receives a StatusCode: 401, ReasonPhrase: 'Unauthorized' response
What am I doing wrong? It's been a real hard time to get this to work.
What worked but it's not what I need
An implicit 2 step authorization flow using frontend for provider authentication calls and a backend call to get my bearer token and create Identity user. With this setup user made a successful login using a social provider, unfortunately it's not what I need
EDIT:
Made a diagram of what is implemented, it is failing at step 5/6 with StatusCode: 401, ReasonPhrase: 'Unauthorized' and further steps are not completed.

The flow you describe pretty much corresponds to "Authorization Cross Domain Code", an OpenID Connect flow that has never been standardized.
I wouldn't recommend going with such a non-standard option. Instead, consider tweaking your flow to make your JS client exclusively communicate with your own authorization server instead of starting the flow by making the client redirect the user agent to an external provider.
The key idea here is that your own authorization server should initiate the initial communication with the external provider (i.e it should build the authorization request and redirect your users to the external provider's authorization endpoint) and handle the last part: the callback authorization response. For that, I'd recommend going with the OAuth2/OIDC handlers shipping with ASP.NET Core (there are providers for Google, Facebook and many more)
Of course, this doesn't mean your JS client can't send a hint about the external provider the user should use to authenticate. It's something you can easily handle in your authorization controller. Here's an example:
public class AuthorizationController : Controller
{
private readonly IAuthenticationSchemeProvider _authenticationSchemeProvider;
private readonly SignInManager<ApplicationUser> _signInManager;
public AuthorizationController(
IAuthenticationSchemeProvider authenticationSchemeProvider,
SignInManager<ApplicationUser> signInManager)
{
_authenticationSchemeProvider = authenticationSchemeProvider;
_signInManager = signInManager;
}
[HttpGet("~/connect/authorize")]
public async Task<IActionResult> Authorize(OpenIdConnectRequest request)
{
Debug.Assert(request.IsAuthorizationRequest(),
"The OpenIddict binder for ASP.NET Core MVC is not registered. " +
"Make sure services.AddOpenIddict().AddMvcBinders() is correctly called.");
if (!User.Identity.IsAuthenticated)
{
// Resolve the optional provider name from the authorization request.
// If no provider is specified, call Challenge() to redirect the user
// to the login page defined in the ASP.NET Core Identity options.
var provider = (string) request.GetParameter("identity_provider");
if (string.IsNullOrEmpty(provider))
{
return Challenge();
}
// Ensure the specified provider is supported.
var schemes = await _authenticationSchemeProvider.GetAllSchemesAsync();
if (!schemes.Any(scheme => scheme.Name == provider))
{
return Challenge();
}
// When using ASP.NET Core Identity and its default AccountController,
// the user must be redirected to the ExternalLoginCallback action
// before being redirected back to the authorization endpoint.
var properties = _signInManager.ConfigureExternalAuthenticationProperties(provider,
Url.Action("ExternalLoginCallback", "Account", new
{
ReturnUrl = Request.PathBase + Request.Path + Request.QueryString
}));
return Challenge(properties, provider);
}
// ...
}
}

Related

JWT authentication and login path

I have a MVC web application that uses JWT token to authenticate users. It calls an authentication/login API to generate and retrieve the token. Since we are in a single sign on setup there won't be a login page that I can redirect the users to. The authentication happens behind without users knowing based on some information. The only way I have found to make this API login call is by decorating the controller(s) in the web application with an Authorization filter and making the httpclient call to the login API in the filter. Is there any way to configure the login URL globally in the MVC application so that every controller will be protected. Something like the following in the cookie authentication that can be configured for the JWT authentication.
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(options =>
{
options.LoginPath = "/User/Login";
options.LogoutPath = "/User/Logout";
});
Or what are the other places where I can make this httpClient call to the API to retrieve the token and subsequently authenticate the users. Let me know if what I explained here doesn't make sense.

Get user info and other claims in Azure Function with Identity server

I am having an application where Authentication is done using IdentityServer4 with Azure AD as an OpenID provider. IdentityServer4 is hosted in Azure App service. After successful authentication, I am able to get access token in Angular application. Access token is passed to .Net Core based RESTful API which is hosted in Azure Function 3.x.
In my Azure function I would like to get user info and other claims without hitting the end point "/connect/userinfo" of IdentityServer4.
Something similar to following for getting Claims would be helpful
[FunctionName("MyFunctionName")]
public static HttpResponseMessage Run(
[HttpTrigger(
AuthorizationLevel.Anonymous,
"get", "post",
Route = "MyFunctionName")]HttpRequestMessage req,
ILogger log,
ClaimsPrincipal claimsPrincipal)
{
// My function code here...
}
How do I get I user info and other claims in Azure function where Authentication is done by IdentityServer4 with Azure AD as OpenID provider
If you have a valid access token, then you can make a request on your own to the UserInfo endpoint to retrieve the remaining user details.
Read more about it here
The only option if you don't want to access the userinfo endpoint is to include the required data in the tokens directly. Here you need to do a trade-of between token size vs convenience. Then you get a really stateless system.
If you don't want to hit user info end point of Identity Server to get the user info and other claims, here is what needs to be done.
Add the claim info to the authorization token
Parse the authorization token and extract the user info and other claim information.
The downside of this approach is that the token size is increased but advantage is that you don't need hit userinfo end point which saves your http request(s). So there are trade offs between each approach.
Here is how you can add claims info while configuring your api in Identity Server. Typically this information resides in Config.cs if you have used Identity Server template
public static IEnumerable<ApiResource> GetApis()
{
var apiResourceList = new List<ApiResource>
{
new ApiResource(IdentityServerConstants.LocalApi.ScopeName)
{
UserClaims =
{
JwtClaimTypes.Email,
JwtClaimTypes.PhoneNumber,
JwtClaimTypes.GivenName,
JwtClaimTypes.FamilyName,
JwtClaimTypes.PreferredUserName
},
}
};
return apiResourceList;
}
For parsing and validating the token please follow the blog Manual token validation in Azure Function
This StackOverflow thread is also very useful.

Identity Server 4 User management API

I've managed to get a solution working with a single page application project (ReactJS), an API running on ASP.net Core project and an IdentityServer 4 project.
I want to be able to call an API on the IdentityServer 4 project from the single page application project.
I created a simple controller class in the IdentityServer 4 application, with the authorize attribute. If I call it via Postman, however, I get the HTML for the login page back.
This happens after I already logged in on the API, and I use that same token.
How am I supposed to log in to identity server to make calls to it to manage users?
As stated in the comments, you should definitely add more information to your question. Your controller is part of the identity server's mvc application? Are you using AspnetCore.Identity?
If so, your controller is protected by AspnetCore.Identities's cookie authentication scheme. You need to send the cookie to access the controller. This has nothing to do with identity server as you are on the local MVC application, it's just plain vanilla MVC.
Postman has problems sending cookies, you need the interceptor chrome extension. You also need to login though postman.
This will probably work if the SPA is hosted by the same MVC application. If not you will need to configure your mvc applications to validate access tokens (not just issue them), like this:
// Adds IdentityServer
app.UseIdentityServer();
// Accept access tokens from identity server
app.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions
{
Authority = "http://localhost:5000",
RequireHttpsMetadata = false,
AuthenticationScheme = "Bearer"
ApiName = "api1"
});
This way you can request access tokens for your local api through identity server, (You also need to configure an api scope for 'api1').
To validate them in your api-controller add this to your controller:
[Authorize(ActiveAuthenticationSchemes = "Bearer")]
[HttpGet]
public string Bearer()
{
return "Access Token Accepted";
}

How to authenticate from automated tests against WebAPI with Owin security?

I have a .net web api app with owin security. The security middleware is configured with Google authentication (no local username/password authentication) and Oauth bearer token.
In startup:
public void ConfigureAuth(IAppBuilder app)
{
...
app.UseOAuthBearerTokens(new OAuthAuthnorizationServerOptions {
TokenEndpointPath = new PathString("/token"),
AuthorizeEndpointPath = new PathString("/account/authorize"),
Provider = new ApplicationOAuthProvider("web"),
...
});
app.UseGoogleAuthentication(new GoogleOAuth2AuthenticationOptions {
ClientID = "...",
ClientSecret = "..."
});
}
From my client applications I go to
http://myapp.com/account/authorize?client_id=web&response_type=token&redirect_uri=...
This redirects to a google login that the user fills in, then back to my app with a bearer token that I peel off and add to request headers for my api.
I would like to write some integration tests where my tests call the API. The tests have the google credentials of a test user, but I am not sure how to have the test authenticate without bringing up a browser and a google login screen. I could drive that with Selenium but that seems heavy-handed.
How do I programmatically get a bearer token to use with my API such that I can impersonate my test user?
Normally you would use the OAuth2 resource owner password credentials grant in this case, but Google does not seem to support that.
So your best option seems to be to use the authorization code grant to authenticate the test user, extract the refresh token from the token endpoint response. Then use the refresh token and client ID and secret to get an access token from your integration test.
See this for more information:
https://developers.google.com/identity/protocols/OAuth2InstalledApp

How to set up cookie based authentication with NancyFx and IdentityServer3 (non-API website)

We have an environment with the following:
Standalone IdentityServer3 instance (issues reference tokens, not jwt)
ASP.NET WebAPI resource server
.NET client applications that authenticate against IdSvr (via resource owner flow)
...and now we'd like to start adding an OWIN-hosted web app that will use NancyFx to serve server-rendered pages as well as a couple AngularJS SPAs. This Nancy website will NOT host any APIs, but may consume data from our existing API. I'd like to add authentication in the OWIN pipeline to help secure our Angular applications from being sent down to users who don't have access.
This would be in contrast to sending down the SPA code, and having Angular determine if the user should see anything. In that case we've already exposed the javascript code base, and this we want to avoid.
I'm trying to understand how I should configure this Nancy site to authenticate users against IdentityServer using the implicit flow. I have implemented this authentication scheme in standalone SPAs before (where all authentication was handled by AngularJS code and tokens were stored in HTML5 local storage), but I'm a bit lost on how to properly tackle this within the OWIN pipeline.
I'm thinking that the OWIN cookie authentication middle-ware is the answer, but does that mean the following?
I need to redirect the user to IdentityServer (using the proper url arguments for implicit flow)?
IdentityServer will redirect the user back to my site on a successful login, so is that where I hook into the OWIN Authorization manager to set the appropriate cookie?
...or am I thinking about this all wrong?
For reference, I've read through the following posts, and they're very helpful but I'm not quite seeing the big picture with OWIN. I'm going to experiment with the UseOpenIdConnectAuthentication middle-ware next, but I would appreciate any guidance SO might have here.
http://brockallen.com/2013/10/24/a-primer-on-owin-cookie-authentication-middleware-for-the-asp-net-developer/
https://github.com/IdentityServer/IdentityServer3/issues/487
Fundamentally, implementing OpenID Connect authentication in a Nancy app hosted via OWIN is really not different from implementing it in any MVC/Katana app (the Thinktecture team has a sample for this scenario: https://github.com/IdentityServer/IdentityServer3.Samples/tree/master/source/Clients/MVC%20OWIN%20Client)
You basically need 3 things: the cookie middleware, the OpenID Connect middleware and the Nancy middleware:
public class Startup {
public void Configuration(IAppBuilder app) {
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions {
AuthenticationMode = AuthenticationMode.Active,
AuthenticationType = CookieAuthenticationDefaults.AuthenticationType
});
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions {
AuthenticationMode = AuthenticationMode.Active,
// Set the address of your OpenID Connect server:
Authority = "http://localhost:54541/"
// Set your client identifier here:
ClientId = "myClient",
// Set the redirect_uri and post_logout_redirect_uri
// corresponding to your application:
RedirectUri = "http://localhost:56765/oidc",
PostLogoutRedirectUri = "http://localhost:56765/"
});
app.UseNancy(options => options.PerformPassThrough = context => context.Response.StatusCode == HttpStatusCode.NotFound);
}
}
If you're looking for a functional demo, you can take a look at https://github.com/aspnet-contrib/AspNet.Security.OpenIdConnect.Server/tree/dev/samples/Nancy/Nancy.Client (note: it doesn't use IdentityServer3 for the OIDC server part but it shouldn't make any difference for the client app).