Browse Source

implemented implicit flow, added user claim to id token

Signed-off-by: Stephan Richter <s.richter@srsoftware.de>
main
Stephan Richter 2 months ago
parent
commit
eec87f678d
  1. 5
      de.srsoftware.oidc.app/src/main/java/de/srsoftware/oidc/app/Application.java
  2. 29
      de.srsoftware.oidc.backend/src/main/java/de/srsoftware/oidc/backend/ClientController.java
  3. 3
      de.srsoftware.oidc.backend/src/main/java/de/srsoftware/oidc/backend/TokenController.java
  4. 3
      de.srsoftware.oidc.web/src/main/resources/en/scripts/authorization.js

5
de.srsoftware.oidc.app/src/main/java/de/srsoftware/oidc/app/Application.java

@ -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());

29
de.srsoftware.oidc.backend/src/main/java/de/srsoftware/oidc/backend/ClientController.java

@ -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);
} }

3
de.srsoftware.oidc.backend/src/main/java/de/srsoftware/oidc/backend/TokenController.java

@ -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

3
de.srsoftware.oidc.web/src/main/resources/en/scripts/authorization.js

@ -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;
} }

Loading…
Cancel
Save