IdentityServer4 as Web API - asp.net-core

I'm newbie in IdentityServer4. Now I try to realize microservices with JWT-authorization (by passing tokens each request to the API). So, the schema is primitive - IDS4 server as an auth server, ASP.NET Identity + MSSQL for client data usage and storage, other services use auth service for token validation.
So, I've looked many articles, but found none of example, where I can customize IDS4 behavior.
For example, I want client to call AuthorizeByLogin(AuthorizeView model) API method, realized in the IDS4 project, where model is an object of 2 fields: Username, Password. In this method I want to check user in the Database and generate access_token, which is passed to the client for working with protected API.
But there is nowhere an example how to do this (call API method, pass the object and receive token). Most of it says "use */connect/token for this".
Could anyone give me an example of code in which this way is realized well? Or maybe tell me which interfaces I should implement and correctly pass to the services in ASP.NET Core app for Authentication Web API + IdentityServer4 realization?
Thank you.

You can find a plethora of quickstart examples in the Identity Server 4 docs. Also from what I understand, you are wanting to use ResourceOwnerCredentials grant type. You won't be able to easily modify the endpoints that issue the tokens without reimplementing majority of Identity Server 4 but you can implement IResourceOwnerPasswordValidator interface and setup a client with an appropriate allowed grant type:
new Client
{
ClientId = "your_client",
AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
ClientSecrets =
{
new Secret("your_password".Sha256())
},
AllowedScopes = { ... }
}
After that, the clients can call connect\token endpoint by providing user name and password of a given user alongside their own credentials:
var tokenResponse = await client.RequestPasswordTokenAsync(new PasswordTokenRequest
{
Address = disco.TokenEndpoint,
ClientId = "ro.client",
ClientSecret = "secret",
UserName = "alice",
Password = "password",
Scope = "api1"
});

Related

How we can authorize user in front of API with IdentityServer

I am dealing with IdentityServer and the idea is that it will become central place for authorizing and authenticating all users. After registration all users will have limited access. There is a separate API which is under development at the moment by different team and they need to protect it. I thought that this is perfect case for the scope. I will define client for for them with defined scope let's say "todolist".
var scope = new ApiScope("todolist");
var client = new Client
{
// ...
AllowedScopes = { "todolist" }
}
The thing is that adding the scope we are authorizing clients, not users. How we can protect the API, so only allowed users can access it? I am wondering if there is need a specific claim with secret code which they can validate.

Authenticating requests to an API based on tokens that originate from a different API

I'm building an API (API 1) that acts as an extension of another API (API 2).
API 1 essentially provides additional functionality that does not exist in API 2.
API 2 contains some core methods that are called by API 1, and also happens to serve as the source of truth for user credentials.
What I'm thinking of doing is this: every time a client authenticates with API 1 (i.e. login), I want to "relay" that call to API 2 to retrieve a bearer token and then pass that back to the requesting client. From that point on, I'd like API 1 to be able to accept/validate that bearer token (which originated from API 2) for any subsequent calls to its own controller methods. How would API 1 actually validate the token if it didn't actually create it?
My first thought is to write a custom handler somewhere in API 1 that somehow asks API 2 to validate the incoming token. Does this approach make sense given what I've described above? If so, how do I get started? I don't know too much about implementing custom OAuth or JWT inside a .NET 5 API, so I need pointers on what steps I'd need to take to implement this.
I apologize if this question is too ambiguous; I've been trying to wrap my head around this for days now and am not sure where to even begin researching a solution, much less implement it.
You definitely can validate the token issued by API2 in API1. If API2 is some commercial product, like e.g. auth0, all you need to do in API1 (assuming it's a .net core app) is to set up the validation URL. API1-app will get all the validation info from there automatically:
services.AddAuthentication().AddJwtBearer("myscheme", options =>
{
// auth0 authentication
options.Audience = configuration["Auth0Settings:ApiIdentifier"];
options.Authority = configuration["Auth0Settings:Domain"];
})
If API2 is your own app, you likely didn't implement all the oauth stuff with discovery URLs etc., but you should know the secret and you can validate the token in API1 yourself:
.AddJwtBearer("myscheme", options =>
{
var jwks = GetJwksKeyFromWhereeverYouStoreThem();
var audience = configuration["Custom:Audience"];
var domain = configuration["Custom:Domain"];
options.Audience = audience;
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidIssuer = domain,
ValidateAudience = true,
ValidAudience = audience,
ValidateIssuerSigningKey = true,
IssuerSigningKeys = jwks.Keys,
RequireSignedTokens = true,
RequireExpirationTime = true,
ValidateLifetime = true,
ClockSkew = TimeSpan.Zero,
ValidAlgorithms = new[] { SecurityAlgorithms.YourAlgorithm, },
};
});
And yes, you could proxy the call from API1 to API2 in order to get the token. You would need however to authenticate your API1-app against your API2-app. Depending on what your API2 is, it could involve special grants to issue tokens on behalf of the end-user (you will be passing username and password to API2).
Having said all this, proxying the authentication IMHO is not a good idea. You potentially lose some security already implemented by the API2 by passing your credentials from API1. In the example of auth0 it involves a password grant, which should be used only by trusted applications. Your client should authenticate against the service that issues the bearer. It may seem good at first to proxy the call, but you loose more than you win (all the changes in API2 will influence your API1, you don't get new security features etc. etc.).
You could also add an API gateway to your setup, implemented e.g. with Ocelot. This will take care of authentication and hide your auth logic from your clients. This is IMHO the best way to go, but is often out of scope. Without a gateway the best setup is when one service issues the tokens and validates them and all other services use these tokens and validate them against the first service.

API Authentication using multiple Authentication Providers

This topic is one that feels like it should be documented better - or perhaps I am using the wrong terms when searching.
I have several SPA apps that use various Oauth2 logins
(ie. Okta, Facebook, Google) to authenticate and generate access
tokens.
These apps all access a common API backend (asp.net core). All
requests to the API have the Oauth2 access token attached as an Authorization header.
How do I configure this single backend API to validate these access tokens from one of a variety of providers, without knowing in advance which access token is attached, and decode a user email address that I can use for further authorization purposes?
I have found much documentation on validating tokens from a descrete, known authorization provider, but very little on using multiple providers. With all the apps out there that give you a choice of Oauth2 logons to choose from (StackOverflow among them), I thought this would be a more common problem.
What am I missing!?
It seems like the correct way to address this situation is to build a Custom Authentication Handler as documented here: https://referbruv.com/blog/posts/implementing-custom-authentication-scheme-and-handler-in-aspnet-core-3x
In this Authentication Handler I can decode the token, assert that the issuer is a member of a whitelist, validate the access token using the issuer's public key, and use the rest of the token to build the Identity I need for further authorization.
At least now I have a better idea what to search for, and I'm not completely re-inventing the authentication mechanism!
You will want to identify the user in a consistent way in your APIs, then authorize requests based on the identity + scopes.
This will be very difficult when using many different token providers, as you are finding. Their access tokens are not designed for you to use in your own APIs.
A better mechanism is to use tokens only from your own Authorization Server, to support different login methods but also put your code in control. My Federated Logins blog post has further info.
It turns out I was overthinking this after all.
Since I am dealing with an API backend, all I needed to do was to validate IDP Bearer tokens, not to create them. In the end, I was able to validate 3 ID providers using the folowing simple code:
services.AddAuthentication(OKTA_SCHEME)
.AddJwtBearer(ADFS_SCHEME, options =>
{
options.Authority = adfsConfig.authority;
options.Authority = adfsConfig.authority;
})
.AddJwtBearer(GOOGLE_SCHEME, jwt => jwt.UseGoogle(
clientId: googleConfig.clientId
))
.AddJwtBearer(OKTA_SCHEME, options =>
{
options.Authority = oktaConfig.authority;
options.Audience = oktaConfig.audience;
});
Note that this required the installation of one additional nuget package to simplify the validation of the Google tokens, which don't appear to follow the standard: Hellang.Authentication.JwtBearer.Google.
At this point I can authorize using attributes like:
[Authorize(AuthorizationSchemes = OKTA_SCHEME)]
...or set up policies based on the schemes.
The second part problem was to link my various logons to users in a local database, which I ended up doing using a custom IClaimsTransformation that uses the information populated to ClaimsPrincipal to lookup a the user in my database, and add an "Employee" role claim, if they are found.
public class EmployeeClaims : IClaimsTransformation
{
public Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal)
{
if (!principal.HasClaim(a => a.Type == "EmployeeNumber"))
{
Employee employee = lookupEmployee(principal);
if (employee != null)
{
ClaimsIdentity id = new ClaimsIdentity();
id.AddClaim(new Claim(ClaimTypes.Role, "Employee"));
id.AddClaim(new Claim("EmployeeNumber", employee.EmployeeNumber.ToString()));
principal.AddIdentity(id);
}
}
return Task.FromResult(principal);
}
private Employee lookupEmployee(ClaimsPrincipal principal) {
string issuer = principal.Claims.Single(a => a.Type == "iss").Value;
if (issuer.Contains("google.com"))
...
}
}
This IClaimsTransformation is then registered by:
services.AddScoped<IClaimsTransformation, EmployeeClaims>();
Now I can additionally authorize employees with:
[Authorize(Roles = "Employee")]

How to receive the current user with create react app -auth template on net core 3 and identity server

I have never used identity server before and I was wondering how to receive the current user info if they have been authenticated using net core 3.
Using the barebones create react app -auth weather app template
in the WeatherForecastController we have the authorize attribute:
[Authorize]
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
With the following endpoint:
[HttpGet]
public IEnumerable<WeatherForecast> Get()
Which will return the correct data if you have logged in and been granted a jwt bearer token.
I would like to create an endpoint that:
1. Allows anonymous access(logged in or not so the authorize attribute needs ditching or overriding)
2. It should then try to authenticate the user
3.If the user can be identified then the endpoint will return the correct data else if the user cannot be identified it will return a subset of the data
I'm struggling to retrieve the user who has made the request information, I believe i'm supposed to use GET /connect/userinfo identityserver endpoint with the bearer token as a parameter but I have no idea how to do this. I would like to access the user_id so I can corrispond with the identityUser table.
I have also tried:
var identity = (ClaimsIdentity)User.Identity;
IEnumerable<Claim> claims = identity.Claims;
It just feels like theres a proper way to do this stuff that i'm missing. I'd be grateful for someone to point me in the right direction.
It should then try to authenticate the user
Browsers communicate with web applications. You should have a web application to be able to redirect back to IDS4 to login. What you are have is an API. Later Web applications communicate with web APIs. Read more here
If the user can be identified then the endpoint will return the correct data else if the user cannot be identified it will return a subset of the data
What you are looking for is Authorization, you can manually do this check on the endpoint. You can access current user using ControllerBase.User ass a result of passing a valid JWT tokens to the API. Considering you already setup authentication using code like bellow:
services.AddAuthentication("Bearer").AddJwtBearer("Bearer",
options =>
{
options.Authority = "http://localhost:5000";
options.Audience = "api1";
options.RequireHttpsMetadata = false;
options.TokenValidationParameters = new TokenValidationParameters()
{
NameClaimType = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier"//To set Identity.Name
};
});
I'm struggling to retrieve the user who has made the request information
You can simply use ControllerBase.User to get the current authenticated user on API, no need to call any endpoint.
Update: Setting NameClaimType as posted on code above will set Identity.Name to the user's identifier.

OpenID Connect server. Generate access token based on 3-rd party token (social login)

I had implemented OpenID Connect server that generates access tokens for mobile client based on username/password using OpenIddict.
My next goal was to provide ability to generate Access Token using 3-rd party tokens (social login for example), and I started from integration with Google token, but stuck as cannot find any samples/informations about how to do this.
The only one idea that I currently have is to make request to "/connect/token" endpoint and send Google token in "code" parameter, for example in "google:" format, then override OpenIdConnectServerProvider.DeserializeAuthorizationCode method:
Called when receiving an authorization code. An application may use this context to deserialize the code using a custom format and to skip the default logic using
So I have created own CustomProvider class based on OpenIddictProvider, registered it
services.AddOpenIddict<ApplicationUser, ApplicationRole, ApplicationDbContext, int>()
.Configure(builder =>
{ builder.Provider = new CustomProvider(sp.GetRequiredService<SignInService>()); }
and overridden the DeserializeAuthorizationCode method:
public override async Task DeserializeAuthorizationCode(DeserializeAuthorizationCodeContext context)
{
string code = context.Request.Code;
if (code.StartsWith("google:"))
{
string token = code.Replace("google:", "");
var principal = new GoogleTokenValidator().ValidateToken(token, null).Result;
var ticket = new AuthenticationTicket(principal, new AuthenticationProperties(), "Bearer");
ticket.SetPresenters(context.Request.ClientId);
context.Ticket = ticket;
context.Ticket.Properties.ExpiresUtc = DateTime.UtcNow.AddDays(1);
context.HandleResponse();
await _signInService.Login(principal);
return;
}
else
{
base.DeserializeAuthorizationCode(context);
}
}
where GoogleTokenValidator is a custom class for Google token handling (it makes call to Google User Information Endpoint and generate ClaimsPrincipal), based on "copy-pasted" code from GoogleHandler class in aspnet/Security repo.
In general it is working with some additional hacks, but I have strong feeling that reinventing the wheel...
In general it is working with some additional hacks, but I have strong feeling that reinventing the wheel...
You're not only reinventing the wheel, but you're also implementing something totally non-standard that is not supported (at all) by OpenIddict.
Here's the approach I recommend (which is the one we use in the MVC server sample):
The OAuth2/OpenID Connect client application redirects the user agent to your authorization controller (you can take a look at this controller for an example).
OpenIddict will validate the authorization request and allow your controller to be invoked if it's fully valid.
If the user is not already logged in, your authorization controller will redirect the user to the login endpoint, provided by AccountController. At this point, you're free to propose local authentication (e.g using a username/password couple) or Google authentication (you can use the Google authentication middleware for that). You can even offer 2-FA as part of this login process.
Once the user is logged in (e.g after a registration process and/or an external authentication association), his/her browser is redirected back to the authorization endpoint, where a consent form indicating he/she's about to allow your JS app to access his personal data on his/her behalf is displayed.
When the user allows your client application to access his data, the request is handled by your authorization controller, that calls SignInAsync to inform OpenIddict that an authorization code/access token should be returned to your application.