IdentityServer4 - how does the API Server communicate with Identity Server - identityserver4

We are currently developing an API and IdentityServer with IdentityServer4 github project :
Github repository : https://github.com/IdentityServer/IdentityServer4
Documentation : https://identityserver4.readthedocs.io/en/latest/
And we are thinking about the most efficient and secure way for the API server, or Service Provider (SP) to communicate with the identity Server, or Identity Provider (IdP). We haven't found a lot of informations yet.
This diagram shows us :
That the Service Provider (SP) asks for the Identity Provider (IdP) the User Info (step 5 : User Info retrieval)
(Source : https://www.researchgate.net/figure/OpenID-Connect-Authorization-Code-Flow_fig1_320282638 )
How does the SP asks those informations to the IdP ? Does he, for example, send an HTTP Post "token-info" containing the access token sent by the User ?
If so, how does the IdP understands that this request comes from the SP ?
We suppose we can :
use a specific SSL Certificate between the IdP and the SP and validate it somehow,
and also RSA Sign the hash of the access token with a certificate known by the IdP ?
We also thought about :
registering the SP itself as a specific client of the IdP and a specific claim,
This claim is only given to the SP, and allows it to send a request to ask for user information regarding an access token
Furthermore, has the SP the ability to store the informations of the user regarding the access token given and previously sent to the IdP (in a dictionary of AccessToken / User Infos) with a limited amount of time, equal to the duration of the access token ? This would prevent the Service Provider to constantly ask for user information from an access token if a resource is consumed a certain amount of times during the validity of the access token
We can provide code as well, but we believe this problem is mainly an architectural issue.

The diagram shows how a user authenticates and gets hold of the access token. The SP is registered in the idP (using clientID/secret). The SP's asks the User(Browser) to authenticate by sending a redirect to the browser.
The SP later when it gets the auth-code, makes a separate request behind the scenes to obtain the real access/id-tokens.
Some simplified code to do this exchange can look like this:
/// <summary>
/// This method is called with the authorization code and state parameter
/// </summary>
/// <param name="code">authorization code generated by the authorization server. This code is relatively short-lived, typically lasting between 1 to 10 minutes depending on the OAuth service.</param>
/// <param name="state"></param>
/// <returns></returns>
[HttpPost]
public IActionResult Callback(string code, string state)
{
//To be secure then the state parameter should be compared to the state sent in the previous step
var url = new Url(_openIdSettings.token_endpoint);
var token = url.PostUrlEncodedAsync(new
{
client_id = "authcodeflowclient", //Id of this client
client_secret = "mysecret",
grant_type = "authorization_code",
code_verifier = code_verifier,
code = code,
redirect_uri = "https://localhost:5001/CodeFlow/callback"
}).ReceiveJson<Token>().Result;
return View(token);
}
With the received tokens, its up to the client where to store them. In ASP.NET, the ID-token is used to create the user session cookie and then the ID-token is discarded. The access token can also be stored in this cookie, or you can store it in memory or somewhere else. but this all depends on your needs.
Once the SP got the access token, he can pass that token to an API. But, before the API can verify the signature of the token, it first asks the IdP for its public signing key (GET request) and then the API uses it to verify the token signature.
See this page for details about the token signing and keys.

Related

IdentityServer4 - Calling API from IProfileService implementation

I'm working on an MVC web project which is using IdentityServer4 to authenticate users, the web app then uses an access token provided to a user by IdentityServer (authorization code flow) to call an API. The IdentityServer has been configured to use Azure AD as an external identity provider, which is the primary mechanism for users to login. That's all working great.
Once authenticated, I need to query the web app's database to determine:
If the user account is authorised to login
Retrieve claims about the user specific to the application
The IdentityServer docs (http://docs.identityserver.io/en/latest/reference/profileservice.html) suggest implementing the IProfileService interface for this, which I've done. I want the ProfileService to call the web app's API to retrieve the information about the user to avoid forcing the IdentityServer to need to know about/directly access the database. My problem however, is that calling the API though needs an access token.
Is it possible to retrieve the token for the current user inside the ProfileService's IsActiveAsync / GetProfileDataAsync methods? I can't find solid documentation that identifies if the token is even generated at that point. I'm also a total noob when it comes to authentication/authorization, it's a massive topic!
I had the idea of using the client credentials flow inside the ProfileService to call the API, just to populate that initial token. However, I don't know whether or not that's an absolutely terrible idea... or if there are any better concepts someone could refer me to that I could investigate.
Can anyone point me in the right direction?
Have a look at ITokenCreationService that is part of identityserver4. You can inject that service into your IProfileService implementation and then create a new bearer token with any claims you like.
For example:
protected readonly ITokenCreationService _tokenCreationService;
...
var token = new Token
{
AccessTokenType = AccessTokenType.Jwt,
Issuer = "https://my.identityserver.com",
Lifetime = (int)TimeSpan.FromMinutes(5).TotalSeconds,
Claims = GetClaimsNeededForApiCall()
};
string myToken = await _tokenCreationService.CreateTokenAsync(token);
...
This is not possible to retrieve the access_token for a user within ProfileService.
The profile service is called whenever IdentityServer needs to return claims about a user. This means if you try to generate a token for the user within ProfileService it will call the ProfileService again.

IdentityServer4 - What is the difference between 'RedirectUri' and 'ReturnUrl'

I'm new to both Identity Server and OAuth 2.0 and am struggling to understand what is happening with each network request after I login.
I have set up a very basic authorization code grant login process with identity server that results in the following series of network requests...
The user attempts to access a resource with the [Authorize] attribute on the client (/home/secret)
They are redirected to the login screen
They login
They can now access the protected resource
I'm struggling to understand what is happening at the point the callback url is triggered.
I understand that redirect_uri is an OAuth term that refers to an address on the client the authorization server (identity server in this case) will send the authorization code to (setup against a client with the setting RedirectUris). This accounts for the signin-oidc request above...
...but what about callback? This url appears to be stored as a ReturnUrl parameter after the user is challenged and redirected to the login page.
What is the difference between this ReturnUrl and the standard OAuth redirect_uri?
In OAuth tutorials I've looked at, they describe the key exchange process as follows...
Authorization server checks username and password
Sends authorization_code to redirect_uri
authorization_code, client_id and client_secret sent back from client to authorization server
authorization_code is checked. access_token sent to redirect_uri
access_token is used to access protected resource
I'm struggling to map this process to what Identity Server appears to be doing.
Any help would be much appreciated!
To understand the need for this parameter we must take into account that in the authorization endpoint we can obtain different types of interactions with the user and that they can also be chained one after the other before being redirected to the client application. For example, an interaction could be the presentation of a Login view and, after a successful sign-in, a Consent screen.
User interactions
The conditions of the request will be evaluated to determine the type of interaction that must be presented to the user. These are some of the possible interactions when you access to the /authorize endpoint:
The user must be redirected to the Login view.
The user must be redirected to the Consent view.
The user must be redirected to the Access Denied view.
The request prompt parameter is none but the user is not authenticated or is not active. A login_required error will be returned.
The request prompt parameter is none, consent is requires but there isn't consent yet. A consent_required error will be returned.
...
And these are some of the conditions to determine which interaction should be used:
Prompt mode (login, none, consent)
If the user is or not authenticated
If user is or not active
If client allows or not local logins
...
Where does the url parameter come from
When you configure IdentityServer4 you can configure the name of this parameter by setting the option UserInteraction.LoginReturnUrlParameter. It is also possible to configure the path of the login endpoint:
services.AddIdentityServer(options =>
{
options.UserInteraction.LoginReturnUrlParameter = "myParamName"; // default value = "returnUrl"
options.UserInteraction.LoginUrl = "/user/login"; // default value = "/account/login"
})
And the value of this parameter is the constant value AuthorizeCallback composed of the Authorize constant and "/callback":
public const string Authorize = "connect/authorize";
public const string AuthorizeCallback = Authorize + "/callback";
Where is this parameter used?
In your case your /authorize endpoint has resolved to redirect to the login view. Notice the returnUrl paremeter added to the login url:
/login?ReturnUrl=/connect/authorize/callback
We can see that this parameter is used in the model of the login view. The following code is extracted from the UI proposed by IdentityServer4 Quickstart.UI:
Login (get). Shows the login page
[HttpGet]
public async Task<IActionResult> Login(string returnUrl)
{
// build a model so we know what to show on the login page
var vm = await BuildLoginViewModelAsync(returnUrl); // returnUrl is added to the LoginViewModel.ReturnUrl property
...
return View(vm);
}
Login (post). Validate credentials and redirect to returnUrl:
[HttpPost]
public async Task<IActionResult> Login(LoginInputModel model, string button)
{
...
if (_users.ValidateCredentials(model.Username, model.Password))
{
...
await HttpContext.SignInAsync(isuser, props); // Creates the "idsrv" cookie
...
return Redirect(model.ReturnUrl); // If we come from an interaction we will end up being redirected to ReturUrl
...
}
}
But I guess your question is why doesn't the idp just redirect to the client application using the redirectUri (signin-oidc) instead of using a local returnUrl?
IdentityServer will receive the request again to re-evaluate if other interaction is necessary before redirect to the client application, for example a consent interaction. Successive requests will be authenticated since we already have a session cookie.
These callbacks to /authorize/callback will take into account additional conditions to the previous ones to determine the next interaction, for example:
If there was already consent or not
If user accepted consent
If user denied consent
...
And what about the original url? (your /home/secret)
This time we are in your client app...
The authentication middleware keeps the original request data in a HttpContext.Feature, this way it will be available later in the handler.
context.Features.Set<IAuthenticationFeature>(new AuthenticationFeature
{
OriginalPath = context.Request.Path,
OriginalPathBase = context.Request.PathBase
});
The base class AuthenticationHandler retrieves this feature in two variables:
protected PathString OriginalPath => Context.Features.Get<IAuthenticationFeature>()?.OriginalPath ?? Request.Path;
protected PathString OriginalPathBase => Context.Features.Get<IAuthenticationFeature>()?.OriginalPathBase ?? Request.PathBase;
When the user attempts to access a resource with the [Authorize] attribute on the client (/home/secret) What it does is invoke the challenge action for your remote handler.
This is the code for the challenge action for OpenIdConnectHandler. This code of the challenge is very similar in other handlers: OauthHandler, CookieAuthenticationHandler.
if (string.IsNullOrEmpty(properties.RedirectUri))
{
properties.RedirectUri = OriginalPathBase + OriginalPath + Request.QueryString;
}
So, the original path "/home/secret" is stored in the AuthenticationProperty.RedirectUri. This AuthenticationProperty is encoded in the state parameter to be sent to the /authorize endpoint.
OAuth2 establishes that if the identity provider receives this parameter, it must return the same value in the response:
state
REQUIRED if the "state" parameter was present in the client
authorization request. The exact value received from the
client.
The challenge action redirects to the idp /authorize endpoint with the OAuth2 parameters including state.
When the idp interaction finish it will redirect us back to our client application with the autentication response, which includes the state value.
The remote handler captures this callback (/signin-oidc), decodes the AuthenticationProperties and invokes the SignIn action of the SignIn scheme.
The SignIn scheme is configured in the handler options SignInScheme property or, if not configured, it will use the default scheme for this action (usually "Cookies").
The SignIn invokation occurs in the abstract base class RemoteAuthenticationHandler, base class of all remote handlers. Notice the parameter ticketContext.Properties. This properties include our original url in the RedirectUri property:
await Context.SignInAsync(SignInScheme, ticketContext.Principal, ticketContext.Properties);
The Cookie handler SignIn action creates the session cookie for our client application (it should not be confused with the SSO cookie created in the idp "idsrv") and uses the RedirectUri property to redirect to the original path: /home/secret

IdentityServer4 - Understanding flows and Endpoints. How is it related to OAuth and OpenIDConnect?

I am integrating the security aspect of webapplication. I have decided to use OAuth,
so we have a REST WebApi in AspNet Core 3.0, the client which is a SPA created in React, and the Identity Server 4.0 app which is also in AspNet Core 3.0.
I read that OAuth is created for Authorization and not for Authentication.
For Authentication, seems that exists something else called OpenIDConnect, so the first question that comes to my mind, and on which I cannot find an easy answer is: are OAuth, OpenIDConnect and IdentityServer related technology?
Which is the best solution for authentication, considering that I would like to create users in a SqlServer Database, and if it's possible I would like to use Entity Framework for the porpose?
The flow for my authentication would be:
User writes Username and Password, if they are right he receive the JWT Token, without redirecting him/her to the authorization page.
At this point the problem are:
which is the right endpoint to do this flow:
is it the /authorize or the /token endpoint?
I have a lot of confusion for the questions above.
The second thing, what is the best way to retrieve the user informations?
For example if my endpoint needs to understand from the logged in user what are his data, I think that or I retrieve from the endpoint or from the JWT token.
Even here I have no clue on which is the best.
I read that OAuth is created for Authorization and not for Authentication. For Authentication, seems that exists something else called OpenIDConnect, so the first question that comes to my mind, and on which I cannot find an easy answer is: are OAuth, OpenIDConnect and IdentityServer related technology?
That's right. OAuth was the first one introduced and allows the person requesting it access to the resources (its handing out access tokens). OIDC (OpenID Connect) on the other-side extends this concept by an identity, the authentication part.
The identity token verifies the identity of the person to your application. Instead of providing identity via username + password (i.e. user creating an account on your website), they get redirected to your authentication provider/app and enter their login there and you get an identity token in return (and/or an access token, depending on the flow and scopes you request).
The identity token is an JWT token (or reference token). The JWT token contains all of the users identity information required for your application (user id, email, displayname, age, etc.) and is cryptographically signed. Only the Identity Server knows the key used to sign it up and you can verify it with the public key from the OIDC (IdSrv here) provider.
Reference token works similar, but claims are requested on the server side and cached.
With identity token you can not access the users resources. Example: Facebook.
When you sign in your application with an facebook account, most page will only request identity token to verify that its the same user (instead of using a username / password combination). But with that one, the application can't access your facebook posts or do posts in your name.
If the application requests an access token (token scope), then also an access token will be returned (if the application is allowed to via allowed scopes). You will be asked to grant the permissions to the resources which the application requests.
With that token, the application can read your posts or post in your name.
Which is the best solution for authentication, considering that I would like to create users in a SqlServer Database, and if it's possible I would like to use Entity Framework for the porpose?
Doesn't really matter. Either one can be used, all you really need is the "sid" (subject id) claim and associate that one with your user.
Identity Server can issue both, depending on what the client asks (if client asks for id_token response type, it will receive an identity token, if it asks for token an access token. Both can be specified or just one).
At this point the problem are: which is the right endpoint to do this flow: is it the /authorize or the /token endpoint? I have a lot of confusion for the questions above.
/authorize is used to authorize the user (have him login, and send back to your website). Its used for so called interactive flows, where the user enters credentials
/token endpoint you can only retrieve a token (resource owner flow (username + password), client credentials (for machine to machine authentication), refresh token (to get a new access token by using an refresh token (if you asked for offline_access scope, which gives and refresh token)
The second thing, what is the best way to retrieve the user informations?
the /userinfo endpoint, see docs: http://docs.identityserver.io/en/latest/endpoints/userinfo.html
As the doc says to access that, the client needs to request the openid scope.
For example if my endpoint needs to understand from the logged in user what are his data, I think that or I retrieve from the endpoint or from the JWT token.
Yes you can retrieve it from JWT token, if you use JWT token. If you use reference token, its just an ID.
And last but not least the /introspection endpoint can be used to validate the token (if your consuming application has no libraries to decrypt and validate signature of the token.
If you can, its best to use the Identity Server client libraries (i.e. IdentityServer4.AccessTokenValidation package for ASP.NET Core or oidc-client for npm/javascript based applications) which should be picking up the correct endpoints, so you don't have to worry about it

Do we really need id_token in implicit flow in OIDC?

I'm working on a SPA application, and I'm using the recommended implicit flow and I'm able to get access_token and id_token. As I need more than the profile info, I've created a separate endpoint to return the user profile information (along with all the other information that's specific to our business) and this endpoint is protected, and can be accessed only with an access_token as the bearer token. Right after getting access_token in SPA, I call this endpoint to get all the user information (which includes first name, last name etc., that gets displayed on the UI). If there is any problem with the returned id_token and access_token pair, the user profile info endpoint call would fail. So, do I really need to validate the id_token? as I'm not relying on any information within that token.
For authenticating against external login provider or authorization code flow, validating the id_token makes sense, but in my case I'm not sure about it.
According to OpenID spec:
When using the Implicit Flow, the contents of the ID Token MUST be validated in the same manner as for the Authorization Code Flow, as defined in Section 3.1.3.7, with the exception of the differences specified in this section.
1. The Client MUST validate the signature of the ID Token according to JWS [JWS] using the algorithm specified in the alg Header Parameter of the JOSE Header.
2. The value of the nonce Claim MUST be checked to verify that it is the same value as the one that was sent in the Authentication Request. The Client SHOULD check the nonce value for replay attacks. The precise method for detecting replay attacks is Client specific.
Why don't you utilise response_type parameter in authorisation request. By changing its value, you can alter what you receive for authorisation response.
Identity documentation mention about possible response type values. Following is an extraction from their documentation,
As you can see, if you do not want SPA to receive the id token, you can set the response_type value to token. If you do so you will only get an access token, which is enforced by OAuth 2.0 specification. (See the OAuth 2.0's implicit flow response_type explanation from here). Note that when you use response_type=token , you are using OAuth 2.0 (not OIDC)
I don't see any wrong in your approach as long as you utilise features enabled by respective protocols.

How to interact with back-end after successful auth with OAuth on front-end?

I want to build small application. There will be some users. I don't want to make my own user system. I want to integrate my application with oauth/oauth2.0.
There is no problem in integration of my front-end application and oauth 2.0. There are so many helpful articles, how to do this, even on stackoverflow.com. For example this post is very helpful.
But. What should I do after successful authorization on front-end? Of course, I can just have flag on client, which says "okay, mate, user is authenticated", but how I should interact with my backend now? I can not just make some requests. Back-end - some application, which provides API functions. EVERYONE can access this api.
So, I need some auth system anyway between my FE and BE. How this system should work?
ps I have some problems with English and may be I can not just correctly 'ask google' about it. Can you provide correct question, please :) or at least give some articles about my question.
UPD
I am looking for concept. I don't want to find some solution for my current problem. I don't think it is matters which FE and BE I use (anyway I will
provide information about it below)
FE and BE will use JSON for communication. FE will make requests, BE will send JSON responses. My application will have this structure (probably):
Frontend - probably AngularJS
Backend - probably Laravel (laravel will implement logic, also there is database in structure)
Maybe "service provider" like google.com, vk.com, twitter.com etc remembers state of user? And after successful auth on FE, I can just ask about user state from BE?
We have 3 main security concerns when creating an API.
Authentication: An identify provider like Google is only a partial solution. Because you don't want to prompt the user to login / confirm their identity for each API request, you must implement authentication for subsequent requests yourself. You must store, accessible to backend:
A user's ID. (taken from the identity provider, for example: email)
A user token. (A temporary token that you generate, and can verify from the API code)
Authorization: Your backend must implement rules based on the user ID (that's your own business).
Transport security: HTTPS and expiring cookies are secure and not replayable by others. (HTTPS is encrypting traffic, so defeats man-in-the-middle attacks, and expiring cookies defeats replay attacks later in time)
So your API / backend has a lookup table of emails to random strings. Now, you don't have to expose the user's ID. The token is meaningless and temporary.
Here's how the flow works, in this system:
User-Agent IdentityProvider (Google/Twitter) Front-End Back-End
|-----------------"https://your.app.com"---------->|
|---cookies-->|
your backend knows the user or not.
if backend recognizes cookie,
user is authenticated and can use your API
ELSE:
if the user is unknown:
|<--"unknown"-|
|<----"your/login.js"----------+
"Do you Authorize this app?"
|<------------------+
|--------"yes"----->|
+----------auth token--------->|
|<---------/your/moreinfo.js---|
|-------access_token ---------->|
1. verify access token
2. save new user info, or update existing user
3. generate expiring, random string as your own API token
+----------->|
|<-------------- set cookie: your API token --------------------|
NOW, the user can directly use your API:
|--------------- some API request, with cookie ---------------->|
|<-------------- some reply, depends on your logic, rules ------|
EDIT
Based on discussion - adding that the backend can authenticate a user by verifying the access token with the identity provider:
For example, Google exposes this endpoint to check a token XYZ123:
https://www.googleapis.com/oauth2/v3/tokeninfo?id_token=XYZ123
I read through all the answers very carefully, and more than half the people who responded are missing the question completely. OP is asking for the INITIAL connection between FE & BE, after the OAuth token has been issued by the Service Provider.
How does your backend know that the OAuth token is valid? Well keep in mind that your BE can send a request to the Service Provider & confirm the validity of the OAuth token, which was first received by your FE. This OAuth key can be decrypted by the Service Provider only because only they have the secret key. Once they decrypt the key, they usually will respond with information such as username, email and such.
In summary:
Your FE receives OAuth token from Service Provider after user gives authorization. FE passes OAuth token to BE. BE sends OAuth token to Service Provider to validate the OAuth token. Service Provider responds to BE with username/email information. You can then use the username/email to create an account.
Then after your BE creates the account, your BE should generate its own implementation of an OAuth token. Then you send your FE this OAuth token, and on every request, your FE would send this token in the header to your BE. Since only your BE has the secret key to validate this token, your application will be very safe. You could even refresh your BE's OAuth token on every request, giving your FE a new key each time. In case someone steals the OAuth token from your FE, that token would be quickly invalidated, since your BE would have already created a new OAuth token for your FE.
There's more info on how your BE can validate the OAuth token. How to validate an OAuth 2.0 access token for a resource server?
let's use OAuth concept to begin,FE here is Client , BE here is Resource Server.
Since your client already authorized, Authorization server should grant
Access token to the client.
Client make request to the resource server with the Access token
Resource server validate the Access token, if valid, handle the request.
You may ask, what is the Access token, Access token was issued by authorization server, grant to client, and recognized by resource server.
Access token is a string indicate the authorization information(e.g. user info, permission scope, expires time...).
Access token may encrypted for security, and you should make sure resource server can decrypt it.
for more details, please read OAuth2.0 specification https://www.rfc-editor.org/rfc/rfc6749.
Well you don'y need User-System on your Front End side.
The front end is just a way to interact with your server and ask for token by valid user and password.
Your server supposed to manage users and the permissions.
User login scenario
User asking for token by entering his username and password.
The server-API accept the request because it's anonymous method (everyone can call this method without care if he's logged in or not.
The server check the DB (Or some storage) and compare the user details to the details he has.
In case that the details matches, the server will return token to the user.
From now, the user should set this token with any request so the server will recognize the user.
The token actually hold the user roles, timestamp, etc...
When the user request for data by API, it fetch the user token from the header, and check if the user is allowed to access that method.
That's how it works in generally.
I based on .NET in my answer. But the most of the BE libaries works like that.
As am doing a project for SSO and based on my understanding to your question, I can suggest that you create an end-point in your back-end to generate sessions, once the client -frontend- has successfully been authorized by the account owner, and got the user information from the provider, you post that information to the back-end endpoint, the back-end endpoint generates a session and stores that information, and send back the session ID -frequently named jSessionId- with a cookie back to the client -frontend- so the browser can save it for you and every request after that to the back-end considered an authenticated user.
to logout, simply create another endpoint in the back-end to accepts a session ID so the back-end can remove it.
I hope this be helpful for you.
You need to store the token in the state of your app and then pass it to the backend with each request. Passing to backend can be done in headers, cookies or as params - depends on how backend is implemented.
Follow the code to see a good example of all the pieces in action (not my code)
This example sets the Authorization: Bearer TOKEN header
https://github.com/cornflourblue/angular-registration-login-example

Resources