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 WellKnownController().bindPath(WELL_KNOWN, "/realms/oidc" + WELL_KNOWN).on(server);
|
||||||
new UserController(mailConfig, sessionService, userService, staticPages).bindPath(API_USER).on(server);
|
new UserController(mailConfig, sessionService, userService, staticPages).bindPath(API_USER).on(server);
|
||||||
var tokenControllerConfig = new TokenController.Configuration(10);
|
var tokenControllerConfig = new TokenController.Configuration(10);
|
||||||
new TokenController(authService, clientService, keyManager, userService, tokenControllerConfig).bindPath(API_TOKEN).on(server);
|
var tokenController = new TokenController(authService, clientService, keyManager, userService, tokenControllerConfig);
|
||||||
new ClientController(authService, clientService, sessionService, userService).bindPath(API_CLIENT).on(server);
|
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 KeyStoreController(keyStore).bindPath(JWKS).on(server);
|
||||||
new EmailController(mailConfig, sessionService, userService).bindPath(API_EMAIL).on(server);
|
new EmailController(mailConfig, sessionService, userService).bindPath(API_EMAIL).on(server);
|
||||||
server.setExecutor(Executors.newCachedThreadPool());
|
server.setExecutor(Executors.newCachedThreadPool());
|
||||||
|
|||||||
@@ -25,12 +25,14 @@ public class ClientController extends Controller {
|
|||||||
private final AuthorizationService authorizations;
|
private final AuthorizationService authorizations;
|
||||||
private final ClientService clients;
|
private final ClientService clients;
|
||||||
private final UserService users;
|
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);
|
super(sessionService);
|
||||||
authorizations = authorizationService;
|
authorizations = authorizationService;
|
||||||
clients = clientService;
|
clients = clientService;
|
||||||
users = userService;
|
users = userService;
|
||||||
|
tokens = tokenController;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -50,15 +52,18 @@ public class ClientController extends Controller {
|
|||||||
var scopes = toList(json, SCOPE);
|
var scopes = toList(json, SCOPE);
|
||||||
if (!scopes.contains(OPENID)) return badRequest(ex, Error.message(ERROR_MISSING_PARAMETER, PARAM, "Scope: openid", STATE, state));
|
if (!scopes.contains(OPENID)) return badRequest(ex, Error.message(ERROR_MISSING_PARAMETER, PARAM, "Scope: openid", STATE, state));
|
||||||
var responseTypes = toList(json, RESPONSE_TYPE);
|
var responseTypes = toList(json, RESPONSE_TYPE);
|
||||||
|
var types = 0;
|
||||||
for (var responseType : responseTypes) {
|
for (var responseType : responseTypes) {
|
||||||
switch (responseType) {
|
switch (responseType) {
|
||||||
case CODE:
|
case CODE:
|
||||||
|
case ID_TOKEN:
|
||||||
|
types++;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return badRequest(ex, Error.message(ERROR_UNSUPPORTED_RESPONSE_TYPE, RESPONSE_TYPE, responseType, STATE, state));
|
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 client = optClient.get();
|
||||||
var redirect = json.getString(REDIRECT_URI);
|
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));
|
if (json.has(NONCE)) authorizations.nonce(user.uuid(), client.id(), json.getString(NONCE));
|
||||||
|
|
||||||
|
|
||||||
var authResult = authorizations.getAuthorization(user.uuid(), client.id(), scopes);
|
var authResult = authorizations.getAuthorization(user.uuid(), client.id(), scopes);
|
||||||
if (!authResult.unauthorizedScopes().isEmpty()) {
|
if (!authResult.unauthorizedScopes().isEmpty()) {
|
||||||
return sendContent(ex, Map.of("unauthorized_scopes", authResult.unauthorizedScopes(), "rp", client.name()));
|
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 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));
|
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());
|
result.put(CODE, authResult.authCode());
|
||||||
if (state != null) result.put(STATE, state);
|
if (state != null) result.put(STATE, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
return sendContent(ex, result);
|
return sendContent(ex, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -127,7 +127,7 @@ public class TokenController extends PathHandler {
|
|||||||
return sendContent(ex, response);
|
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 {
|
try {
|
||||||
PublicJsonWebKey key = keyManager.getKey();
|
PublicJsonWebKey key = keyManager.getKey();
|
||||||
var algo = key.getAlgorithm();
|
var algo = key.getAlgorithm();
|
||||||
@@ -181,6 +181,7 @@ public class TokenController extends PathHandler {
|
|||||||
claims.setClaim(AT_HASH, atHash);
|
claims.setClaim(AT_HASH, atHash);
|
||||||
claims.setClaim(CLIENT_ID, client.id());
|
claims.setClaim(CLIENT_ID, client.id());
|
||||||
claims.setClaim(EMAIL, user.email()); // additional claims/attributes about the subject can be added
|
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));
|
optNonce.ifPresent(nonce -> claims.setClaim(NONCE, nonce));
|
||||||
claims.setGeneratedJwtId(); // a unique identifier for the token
|
claims.setGeneratedJwtId(); // a unique identifier for the token
|
||||||
|
|||||||
@@ -35,8 +35,9 @@ function handleResponse(response){
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (json.scope){
|
if (json.scope){
|
||||||
|
var separator = json.id_token ? '#' : '?';
|
||||||
var query = Object.keys(json).map(key => `${key}=${encodeURIComponent(json[key])}`).join('&');
|
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);
|
redirect(url);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user