Author: Fiege Polina
Setup Keycloak
Install keycloak using docker image, run following command:
Make hostname sso point to 127.0.0.1:
Figure 1 - /etc/hosts
Access keycloak at sso:8081
Figure 2 - sso:8081
Create realm as_lab
Figure 3 - realm
Create sample user in this realm
Figure 4 - user
Launch sample app - installation instructions inside iam-lab-part1.zip
Figure 4 - app
1. What is client?
Clients are entities that can request Keycloak to authenticate a user. An application or Internet site that the user uses and that interacts with the Authorization Server and Resource Server to obtain the user's private data.
3. What client types exist?
Confidential clients are clients that can keep the client_secret file confidential. Usually these clients are only applications (more often web applications) running on a developer-controlled server where the source code is not available to users.
Public clients cannot keep the client_secret file confidential, so the secret is not used for these applications. Both mobile apps and Javascript apps are considered public clients. Every time an application runs on a device under the user's control, it should be considered a public client.
4. What authentication flows exist?
The OAuth 2.0 Authorization Framework supports several different flows (or grants). Flows are ways of retrieving an Access Token.
5. Why is Authorization Code Flow preferred over the others?
Because we are interested to protect interactive clients (SAP and web-app) from against code substitution and in this case we need to use either a hybrid flow or PKCE should be used. If PKCE is available, this is a simpler solution to the problem.
Unlike providing an authorization code, implicit provision does not redirect the browser back to the backend of your application with an authorization code. Instead, it puts the access token directly into the URL as part of the redirect. Which leads to the danger of a code substitution attack. If we talk about the disadvantages of hybrid flow, they are as follows: can lead to leakage of additional (personally identifiable) data, all mitigation steps (for example, encryption) must be implemented by the client. This leads to a more complex implementation of the client library.
Authorization Code Flow with PKCE is already an official recommendation for
SPA.
6. What is Single-Sign On (SSO)?
This is an authentication method that allows users to securely authenticate to multiple applications and sites at once using a single set of credentials. In fact, SSO is part of a larger concept called Federated Identity Management, so sometimes SSO is referred to as a federated SSO. FIM simply refers to a trust relationship created between two or more domains or identity management systems. A SSO is a feature available within the FIM architecture.
7. What is the difference between OAuth 2.0 and OpenID Connect?
OAuth 2.0 is a special software platform that can also be considered part of the FIM architecture. OAuth focuses on trust relationships by providing user identification information to domains.
OpenID Connect (OIDC) is an authentication layer imposed on the OAuth 2.0 base to ensure the functionality of SSO.
Сreate corresponding client for app-pokemon
:
Figure 5 - New client "app-pokemon"
Create a Role:
Figure 6 - New Role
Create a User in the Сredentials tab, the user needs to set a password:
Figure 7 - New User
Figure 8 - Connect Role with User
Figure 8 - Success authorisation
Then create the appropriate app-trainer client and show that SSO works. I took all the same steps as for app-pokemon.
Figure 9 - Success authorisation
Take a look at full-login process and auto-login process in the network tab of browser devtools and describe what is happening. Use “Preserve log” to prevent resetting on redirects.
It is the Authorization Code Flow.
Figure 10 - Browser Flow
After reviewing a large amount of information, this answer on the old forum seems to me the most human:
The ID token confirms your identity, but does not give you permissions to manipulate resources. While the access token is associated with permissions and client accesses to the resource server.
The ID tokens are intended to be read by the OAuth client. Access tokens are intended to be read by the resource server.
For example user with its browser authenticate against an OpenID provider and gets access to a web application. The result of that authentication process based on OpenID Connect is the ID token, which is passed to the application as proof that the user has been authenticated.
In the OAuth 2 context, the access token allows a client application to access a specific resource to perform specific actions on behalf of the user. That is what is known as a delegated authorization scenario: the user delegates a client application to access a resource on their behalf.
Figure 11 - /openid-connect/token
The purpose of an update token is its ability to update a pair (the access token and itself). When the access token lifetime comes to an end, the application uses refresh token to update both tokens and continue using the new access token. The refresh token is one-time and often lives longer than the access token (if the access token lives 5 minutes in our application, then the time of the update token will be significantly longer)
The refresh + access token scheme limits the time for which an attacker can access the service. Compared to one token, which an attacker can use for weeks and no one will know about it.
The session is saved by transmitting cookies such as, for example, the KC_RESTART
cookie, which is created at the beginning of the authentication flow. It contains information about the client encoded in the JWS token. The cookie is used when the root authentication session expires and to create a new authentication session based on the client information provided in the cookie. Further, through mitmproxy, you can see that the token is loaded immediately upon a new logging attempt. if you switch from the main application to the Pokemon application every time, for example, and click on the "log in" button (not to log out, but just close the tab), then mitmproxy will constantly collect such calls:
Figure 12 - Persisted session
In the new session, everything is as in the picture, there are a lot of calls to the keycloak, getting a new token, also setting cookies.
Figure 13 - New session
4.1 Keycloak issues access tokens in JWT format. The advantage of JWT token is that all information that is required to make a decision about authorization can be extracted and verified from it.
1. What is the format of JWT token? How can you decode it, is there any standard tools?
As I understood from the header above, you are asking me about access tokens (not about the ID token). In this case, I will answer you that the Bearer Token format is used.
Bearer Tokens are the predominant type of access token used with OAuth 2.0. The basic OAuth 2 specifications say nothing about the format of the access token. It can be a string of any format. In our case, it's JWT, okay. In simple words, JWT is just a string in the following format header.payload.signature
.
Figure 14 - Access Token
As for standard methods, perhaps you mean to do it directly with Keycloack tools. We use any Base64 decoder for decoding. In any case, here is a very comprehensive answer about the ways of researching this token.
2. How is JWT token issued and verified?
First, the user logs into the authentication server using an authentication key (for example, a login/password pair). The authentication server then creates a JWT and sends it to the user. When a user makes a request to the application API, he adds the previously received JWT to it. In this moment the application can check by the JWT passed with the request whether the user is who he claims to be. This happens as follows: the application server receives a secret key from the authentication server during the installation of authentication processes. Since the application knows the secret key when the user makes an API request with a token attached to it, the application can repeat the signing algorithm to the JWT. The application can then verify this signature by comparing it with its own, calculated by hashing. If the signatures match, then the JWT is valid, that is, it came from a verified source.
3. What is claim and what is scope?
Assertions are a payload of a name/value pair that contain information about the user. Scope is the scope of the token, which determines the possibilities of its use in relation to user data. Each scope returns a set of user attributes, which are called claims. The scopes an application should request depend on which user attributes the application needs. For example, you can use a token with scope: profile email openid
to send a personalized welcome letter to the user.
4. What is aud claim?
One important claim is theaud
claim. This claim defines the audience of the token, i.e., the web application that is meant to be the final recipient of the token. This claim is only in the ID token.
Figure 15 - theaud
claim
4.2 Look at how the api-pokemon
makes authorization decision and list the critical claims that are required for positive decision.
File: server/verify-access.ts
Figure 16 - verify-access.ts
First of all, we need to make sure that in addition to the Bearer type, the token also contains the necessary: we check the header
Actually, below, in the try handler, you can see how the check itself happens with this line:
(Synchronous) If a callback is not supplied, function acts synchronously. Returns the payload decoded if the signature is valid and audience are valid. Next comes the recognition of the payload, which is an service role. It can be seen from the code that the most critical thing in the payload is the presence of roles, or if be more correct at least one allowed role.
Critical claims that are required for positive decision:
4.3 Create corresponding clients and add roles
I found the specified roles in the file along the path:
Figure 17 - Roles
After that I created a new client api-pokemon
. Also Keycloak provides a special bearer-only type of client - it is for services that do not initiate user login.
Figure 18 - New client
And added two roles "editor" and "readonly"
Figure 19 - New roles
Note that for app-pokemon you already have associated client in Keycloak. In this case, I already have a ready-made user for this client.
Figure 20 - Role Mappings
Make changes to Keycloak such that correct required claims are included into access token. Also, for debugging purposes Keycloak’s Admin console provides Evaluation tool that shows you what is included inside access token (I will use it later).
For valid JWT verification do not forget to replace hardcoded cert (in server/verify-access.ts
) with the cert of your realm.
Figure 21 - Cert of my realm
Show that app-pokemon successfully works.
Figures 22,23 - Editor & Readonly
a) What are realm roles and client roles in Keycloak?
Realm-level roles are in global namespace shared by all clients. Client roles have basically a namespace dedicated to a client.
b) What is a composite role?
Any realm or client level role can be turned into a composite role. A composite role is a role that has one or more additional roles associated with it. When a composite role is mapped to the user, the user also gains the roles associated with that composite.
c) How does browser-side app app-pokemon understand that form input should be enabled/disabled?
I think the browser side checks the access token, in which it detects a link to the resources of a certain role. I've looked through all the application files, and it seems to me that the mapping going is just by name. For example, if the editor
role has an IsEditable
variable, then it is assigned to it at the java config level, and therefore I did not immediately understand why I could not change this or that access to the Pokemon profile directly from the keycloak.
Figure 24 - Evalueting tool
5.1 Create another test-client with some-role and add this role to the sample user. After that inspect access_token token for app-pokemon. Is new role automatically included into it?
I have created a new client with a test role. Then I gave the user a Bulbosaurus role for this client.
Figures 25, 26, 27 - Set up
As you can see, the access token of Bulbasaurus to the Pokemon application displays all the clients from whom he owns any role.
Figure 28 - Access token
5.2 Learn how exactly user information that Keycloak possesses is mapped inside to access token. Make changes to Keycloak such that for app-pokemon only api-pokemon roles are mapped to the access token, even if he has other roles.
To change this default behavior, you must explicitly turn off the Full Scope Allowed
switch and declare the specific roles you want in each individual client.
Figures 29, 30 - Set up & Result
a) How is claim added to access token?
The default profile service of ASP.Net Identity searches for all user claims from the database storage. The profile service will use cookie claims (issued by the server during the authentication process) to generate access token claims when the client requests an access token by providing claims from the cookie
In other case, you can simply turn to the Client Scopes tab and see what user data the access token carries with it. These scopes are defined globally for all clients, there are those that are used by default, and there are optional ones. We can also create our own global scopes. For example, I can use the user's banal email address (because it is defined by default in the user's credentials).
Figures 31, 32 - Client Scopes
Make changes to Keycloak such that access token for issued for app-pokemon
will include favorite_color
user attribute.
To do this, I need to go to the client
, select the Mappers
tab, and create a new favorite_color
mapper in it. I am creating it exclusively for the app-pokemon, this mapper will not be displayed to other clients. After that, I need to create an attribute associated with this mapper from the user himself.
Figure 33 - New mapper
I can also choose which type of token this claim will be displayed in.
Figure 34 - New attribute
As you can see, this claim is now present in the token of this user, although it is not information from the global client scopes.
Figure 35 - Result