Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Establish SSO/set cookies with access or id token/token exchange

I'm allowing users logged in an external application to jump into our application with their access token through Keycloak's identity brokering and external to internal token exchange.

Now I'd like to establish an SSO session in an embedded JxBrowser in our application similar to a regular browser login flow, where three cookies are set in the browser: AUTH_SESSION, KEYCLOAK_SESSION(_LEGACY) and KEYCLOAK_IDENTITY(_LEGACY).

KEYCLOAK_IDENTITY contains a token of type Serialized-ID which looks somewhat similar to an ID token.

Is it possible to create the KEYCLOAK_IDENTITY cookie using the exchanged (internal) access and/or ID token and, provided that the other two cookies are correctly created as well, would this establish a valid SSO session?

Basically all I am missing is how I could obtain or create the Serialized-ID type token.

like image 804
Torsten Römer Avatar asked Jul 15 '20 13:07

Torsten Römer


People also ask

What is the difference between token and cookie?

Cookies and tokens are two common ways of setting up authentication. Cookies are chunks of data created by the server and sent to the client for communication purposes. Tokens, usually referring to JSON Web Tokens (JWTs), are signed credentials encoded into a long string of characters created by the server.

How do I get an SSO token?

Use the getAccessToken API to get an access token that contains the identity for the current user signed in to Office. The access token is also an ID token because it contains identity claims about the signed in user, such as their name and email.

How do you authenticate cookies?

The entire cookie-based authentication works in the following manner: The user gives a username and password at the time of login. Once the user fills in the login form, the browser (client) sends a login request to the server. The server verifies the user by querying the user data.


Video Answer


1 Answers

One way to achieve this:

  1. Implement a custom endpoint following this example

Note that the provider works fine for me without registering it in standalone.xml, I'm just adding the JAR to the Keycloak Docker image.

  1. Add a method that validates a given access token, looks up the user, gets the user session and sets the cookies in the response (most error handling omitted for brevity):

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    @Path("sso")
    public Response sso(@Context final HttpRequest request) {
        final HttpHeaders headers = request.getHttpHeaders();
        final String authorization = headers.getHeaderString(HttpHeaders.AUTHORIZATION);
        final String[] value = authorization.split(" ");
        final String accessToken = value[1];
        final AccessToken token = Tokens.getAccessToken(accessToken, keycloakSession);
    
        if (token == null) {
            throw new ErrorResponseException(Errors.INVALID_TOKEN, "Invalid access token", Status.UNAUTHORIZED);
        }
    
        final RealmModel realm = keycloakSession.getContext().getRealm();
        final UriInfo uriInfo = keycloakSession.getContext().getUri();
        final ClientConnection clientConnection = keycloakSession.getContext().getConnection();
    
        final UserModel user = keycloakSession.users().getUserById(token.getSubject(), realm);
    
        final UserSessionModel userSession = keycloakSession.sessions().getUserSession(realm, token.getSessionState());
    
        AuthenticationManager.createLoginCookie(keycloakSession, realm, user, userSession, uriInfo, clientConnection);
    
        return Response.noContent().build();
    }
    

Disclaimer: I am not completely certain this implementation does not imply any security issues, but since Tokens.getAccessToken(accessToken, keycloakSession) does full validation of the access token, setting the cookies should only be possible with a valid access token.

For CORS, add:

@OPTIONS
@Produces(MediaType.APPLICATION_JSON)
@Path("sso")
public Response preflight(@Context final HttpRequest request) {
    return Cors.add(request, Response.ok("", MediaType.APPLICATION_JSON))
            .auth()
            .preflight()
            .allowedMethods("GET", "OPTIONS")
            .build();
}

and in sso():

    return Cors.add(request, Response.ok("", MediaType.APPLICATION_JSON))
            .auth()
            .allowedMethods("GET")
            .allowedOrigins(token)
            .build();

What I am uncertain about is why Firefox preflights the GET request, making it necessary to handle that.

like image 69
Torsten Römer Avatar answered Oct 10 '22 20:10

Torsten Römer