implemented implicit flow, added user claim to id token
Signed-off-by: Stephan Richter <s.richter@srsoftware.de>
This commit is contained in:
@@ -79,8 +79,9 @@ public class Application {
|
||||
new WellKnownController().bindPath(WELL_KNOWN, "/realms/oidc" + WELL_KNOWN).on(server);
|
||||
new UserController(mailConfig, sessionService, userService, staticPages).bindPath(API_USER).on(server);
|
||||
var tokenControllerConfig = new TokenController.Configuration(10);
|
||||
new TokenController(authService, clientService, keyManager, userService, tokenControllerConfig).bindPath(API_TOKEN).on(server);
|
||||
new ClientController(authService, clientService, sessionService, userService).bindPath(API_CLIENT).on(server);
|
||||
var tokenController = new TokenController(authService, clientService, keyManager, userService, tokenControllerConfig);
|
||||
tokenController.bindPath(API_TOKEN).on(server);
|
||||
new ClientController(authService, clientService, sessionService, userService, tokenController).bindPath(API_CLIENT).on(server);
|
||||
new KeyStoreController(keyStore).bindPath(JWKS).on(server);
|
||||
new EmailController(mailConfig, sessionService, userService).bindPath(API_EMAIL).on(server);
|
||||
server.setExecutor(Executors.newCachedThreadPool());
|
||||
|
||||
@@ -25,12 +25,14 @@ public class ClientController extends Controller {
|
||||
private final AuthorizationService authorizations;
|
||||
private final ClientService clients;
|
||||
private final UserService users;
|
||||
private final TokenController tokens;
|
||||
|
||||
public ClientController(AuthorizationService authorizationService, ClientService clientService, SessionService sessionService, UserService userService) {
|
||||
public ClientController(AuthorizationService authorizationService, ClientService clientService, SessionService sessionService, UserService userService, TokenController tokenController) {
|
||||
super(sessionService);
|
||||
authorizations = authorizationService;
|
||||
clients = clientService;
|
||||
users = userService;
|
||||
tokens = tokenController;
|
||||
}
|
||||
|
||||
|
||||
@@ -50,15 +52,18 @@ public class ClientController extends Controller {
|
||||
var scopes = toList(json, SCOPE);
|
||||
if (!scopes.contains(OPENID)) return badRequest(ex, Error.message(ERROR_MISSING_PARAMETER, PARAM, "Scope: openid", STATE, state));
|
||||
var responseTypes = toList(json, RESPONSE_TYPE);
|
||||
var types = 0;
|
||||
for (var responseType : responseTypes) {
|
||||
switch (responseType) {
|
||||
case CODE:
|
||||
case ID_TOKEN:
|
||||
types++;
|
||||
break;
|
||||
default:
|
||||
return badRequest(ex, Error.message(ERROR_UNSUPPORTED_RESPONSE_TYPE, RESPONSE_TYPE, responseType, STATE, state));
|
||||
}
|
||||
}
|
||||
if (!responseTypes.contains(CODE)) return badRequest(ex, Error.message(ERROR_MISSING_CODE_RESPONSE_TYPE, STATE, state));
|
||||
if (types < 1) return badRequest(ex, Error.message(ERROR_MISSING_CODE_RESPONSE_TYPE, STATE, state));
|
||||
|
||||
var client = optClient.get();
|
||||
var redirect = json.getString(REDIRECT_URI);
|
||||
@@ -74,16 +79,32 @@ public class ClientController extends Controller {
|
||||
}
|
||||
if (json.has(NONCE)) authorizations.nonce(user.uuid(), client.id(), json.getString(NONCE));
|
||||
|
||||
|
||||
var authResult = authorizations.getAuthorization(user.uuid(), client.id(), scopes);
|
||||
if (!authResult.unauthorizedScopes().isEmpty()) {
|
||||
return sendContent(ex, Map.of("unauthorized_scopes", authResult.unauthorizedScopes(), "rp", client.name()));
|
||||
}
|
||||
|
||||
var joinedAuthorizedScopes = Optionals.nullable(authResult.authorizedScopes()).map(AuthorizedScopes::scopes).map(list -> String.join(" ", list));
|
||||
var result = new HashMap<String, String>();
|
||||
|
||||
var result = new HashMap<String, Object>();
|
||||
|
||||
joinedAuthorizedScopes.ifPresent(authorizedScopes -> result.put(SCOPE, authorizedScopes));
|
||||
|
||||
if (responseTypes.contains(ID_TOKEN)) {
|
||||
var accessToken = users.accessToken(user);
|
||||
var issuer = hostname(ex);
|
||||
String jwToken = tokens.createJWT(client, user, accessToken, issuer);
|
||||
ex.getResponseHeaders().add("Cache-Control", "no-store");
|
||||
result.put(ACCESS_TOKEN, accessToken.id());
|
||||
result.put(TOKEN_TYPE, BEARER);
|
||||
result.put(EXPIRES_IN, 3600);
|
||||
result.put(ID_TOKEN, jwToken);
|
||||
} else if (responseTypes.contains(CODE)) {
|
||||
result.put(CODE, authResult.authCode());
|
||||
if (state != null) result.put(STATE, state);
|
||||
}
|
||||
|
||||
|
||||
return sendContent(ex, result);
|
||||
}
|
||||
|
||||
|
||||
@@ -127,7 +127,7 @@ public class TokenController extends PathHandler {
|
||||
return sendContent(ex, response);
|
||||
}
|
||||
|
||||
private String createJWT(Client client, User user, AccessToken accessToken, String issuer) {
|
||||
String createJWT(Client client, User user, AccessToken accessToken, String issuer) {
|
||||
try {
|
||||
PublicJsonWebKey key = keyManager.getKey();
|
||||
var algo = key.getAlgorithm();
|
||||
@@ -181,6 +181,7 @@ public class TokenController extends PathHandler {
|
||||
claims.setClaim(AT_HASH, atHash);
|
||||
claims.setClaim(CLIENT_ID, client.id());
|
||||
claims.setClaim(EMAIL, user.email()); // additional claims/attributes about the subject can be added
|
||||
claims.setClaim(USER, user.username());
|
||||
|
||||
optNonce.ifPresent(nonce -> claims.setClaim(NONCE, nonce));
|
||||
claims.setGeneratedJwtId(); // a unique identifier for the token
|
||||
|
||||
@@ -35,8 +35,9 @@ function handleResponse(response){
|
||||
return;
|
||||
}
|
||||
if (json.scope){
|
||||
var separator = json.id_token ? '#' : '?';
|
||||
var query = Object.keys(json).map(key => `${key}=${encodeURIComponent(json[key])}`).join('&');
|
||||
var url = params.get('redirect_uri') + '?' + query.toString();
|
||||
var url = params.get('redirect_uri') + separator + query.toString();
|
||||
redirect(url);
|
||||
return;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user