Browse Source

implemented forwarding errors to UI

Signed-off-by: Stephan Richter <s.richter@srsoftware.de>
main
Stephan Richter 1 month ago
parent
commit
640b49a70b
  1. 124
      de.srsoftware.oidc.api/src/main/java/de/srsoftware/oidc/api/Constants.java
  2. 27
      de.srsoftware.oidc.backend/src/main/java/de/srsoftware/oidc/backend/ClientController.java
  3. 16
      de.srsoftware.oidc.web/src/main/resources/en/authorization.html
  4. 15
      de.srsoftware.oidc.web/src/main/resources/en/scripts/authorization.js

124
de.srsoftware.oidc.api/src/main/java/de/srsoftware/oidc/api/Constants.java

@ -3,63 +3,69 @@ package de.srsoftware.oidc.api;
public class Constants { public class Constants {
public static final String ACCESS_TOKEN = "access_token"; public static final String ACCESS_TOKEN = "access_token";
public static final String APP_NAME = "LightOIDC"; public static final String APP_NAME = "LightOIDC";
public static final String AT_HASH = "at_hash"; public static final String AT_HASH = "at_hash";
public static final String ATTEMPTS = "attempts"; public static final String ATTEMPTS = "attempts";
public static final String AUTH_CODE = "authorization_code"; public static final String AUTH_CODE = "authorization_code";
public static final String AUTHORZED = "authorized"; public static final String AUTHORZED = "authorized";
public static final String BEARER = "Bearer"; public static final String BEARER = "Bearer";
public static final String CAUSE = "cause"; public static final String CAUSE = "cause";
public static final String CLIENT_ID = "client_id"; public static final String CLIENT_ID = "client_id";
public static final String CLIENT_SECRET = "client_secret"; public static final String CLIENT_SECRET = "client_secret";
public static final String CODE = "code"; public static final String CODE = "code";
public static final String EMAIL = "email"; public static final String EMAIL = "email";
public static final String ERROR = "error"; public static final String ERROR = "error";
public static final String CONFIG_PATH = "LIGHTOIDC_CONFIG_PATH"; public static final String CONFIG_PATH = "LIGHTOIDC_CONFIG_PATH";
public static final String CONFIRMED = "confirmed"; public static final String CONFIRMED = "confirmed";
public static final String DAYS = "days"; public static final String DAYS = "days";
public static final String ENCRYPTION_KEY = "encryption_key"; public static final String ENCRYPTION_KEY = "encryption_key";
public static final String ERROR_DESCRIPTION = "error_description"; public static final String ERROR_DESCRIPTION = "error_description";
public static final String ERROR_LOCKED = "error_locked"; public static final String ERROR_INVALID_REDIRECT = "error_invalid_redirect";
public static final String ERROR_LOGIN_FAILED = "error_login_failed"; public static final String ERROR_LOCKED = "error_locked";
public static final String ERROR_NO_USERNAME = "error_no_username"; public static final String ERROR_LOGIN_FAILED = "error_login_failed";
public static final String EXPIRATION = "expiration"; public static final String ERROR_MISSING_PARAMETER = "error_missing_parameter";
public static final String EXPIRES_IN = "expires_in"; public static final String ERROR_MISSONG_CODE_RESPONSE_TYPE = "error_missing_code";
public static final String GRANT_TYPE = "grant_type"; public static final String ERROR_NO_USERNAME = "error_no_username";
public static final String ID_TOKEN = "id_token"; public static final String ERROR_UNKNOWN_CLIENT = "error_unknown_client";
public static final String INVALID_CLIENT = "invalid_client"; public static final String ERROR_UNSUPPORTED_RESPONSE_TYPE = "error_unsupported_response_type";
public static final String INVALID_GRANT = "invalid_grant"; public static final String EXPIRATION = "expiration";
public static final String INVALID_REDIRECT_URI = "invalid_request_uri"; public static final String EXPIRES_IN = "expires_in";
public static final String INVALID_REQUEST = "invalid_request"; public static final String GRANT_TYPE = "grant_type";
public static final String INVALID_REQUEST_OBJECT = "invalid_request_object"; public static final String ID_TOKEN = "id_token";
public static final String INVALID_SCOPE = "invalid_scope"; public static final String INVALID_CLIENT = "invalid_client";
public static final String LANDING_PAGE = "landing_page"; public static final String INVALID_GRANT = "invalid_grant";
public static final String MAILCONFIG = "mail_config"; public static final String INVALID_REDIRECT_URI = "invalid_request_uri";
public static final String NAME = "name"; public static final String INVALID_REQUEST = "invalid_request";
public static final String NONCE = "nonce"; public static final String INVALID_REQUEST_OBJECT = "invalid_request_object";
public static final String PERMISSION = "permission"; public static final String INVALID_SCOPE = "invalid_scope";
public static final String OPENID = "openid"; public static final String LANDING_PAGE = "landing_page";
public static final String REDIRECT_URI = "redirect_uri"; public static final String MAILCONFIG = "mail_config";
public static final String REDIRECT_URIS = "redirect_uris"; public static final String NAME = "name";
public static final String RELEASE = "release"; public static final String NONCE = "nonce";
public static final String REQUEST_NOT_SUPPORTED = "request_not_supported"; public static final String PARAM = "parameter";
public static final String RESPONSE_TYPE = "response_type"; public static final String PERMISSION = "permission";
public static final String SALT = "salt"; public static final String OPENID = "openid";
public static final String SCOPE = "scope"; public static final String REDIRECT_URI = "redirect_uri";
public static final String SECRET = "secret"; public static final String REDIRECT_URIS = "redirect_uris";
public static final String SESSION_DURATION = "session_duration"; public static final String RELEASE = "release";
public static final String SMTP_USER = "smtp_user"; public static final String REQUEST_NOT_SUPPORTED = "request_not_supported";
public static final String SMTP_PASSWORD = "smtp_pass"; public static final String RESPONSE_TYPE = "response_type";
public static final String SMTP_AUTH = "smtp_auth"; public static final String SALT = "salt";
public static final String SMTP_HOST = "smtp_host"; public static final String SCOPE = "scope";
public static final String SMTP_PORT = "smtp_port"; public static final String SECRET = "secret";
public static final String STATE = "state"; public static final String SESSION_DURATION = "session_duration";
public static final String START_TLS = "start_tls"; public static final String SMTP_USER = "smtp_user";
public static final String TOKEN = "token"; public static final String SMTP_PASSWORD = "smtp_pass";
public static final String TOKEN_TYPE = "token_type"; public static final String SMTP_AUTH = "smtp_auth";
public static final String TRUST = "trust"; public static final String SMTP_HOST = "smtp_host";
public static final String UNAUTHORIZED_CLIENT = "unauthorized_client"; public static final String SMTP_PORT = "smtp_port";
public static final String USER = "user"; public static final String STATE = "state";
public static final String USER_ID = "user_id"; public static final String START_TLS = "start_tls";
public static final String TOKEN = "token";
public static final String TOKEN_TYPE = "token_type";
public static final String TRUST = "trust";
public static final String UNAUTHORIZED_CLIENT = "unauthorized_client";
public static final String USER = "user";
public static final String USER_ID = "user_id";
} }

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

@ -3,7 +3,6 @@ package de.srsoftware.oidc.backend;
import static de.srsoftware.oidc.api.Constants.*; import static de.srsoftware.oidc.api.Constants.*;
import static de.srsoftware.oidc.api.data.Permission.MANAGE_CLIENTS; import static de.srsoftware.oidc.api.data.Permission.MANAGE_CLIENTS;
import static de.srsoftware.utils.Optionals.emptyIfBlank;
import static java.net.HttpURLConnection.*; import static java.net.HttpURLConnection.*;
import com.sun.net.httpserver.HttpExchange; import com.sun.net.httpserver.HttpExchange;
@ -12,6 +11,7 @@ import de.srsoftware.oidc.api.data.AuthorizedScopes;
import de.srsoftware.oidc.api.data.Client; import de.srsoftware.oidc.api.data.Client;
import de.srsoftware.oidc.api.data.Session; import de.srsoftware.oidc.api.data.Session;
import de.srsoftware.oidc.api.data.User; import de.srsoftware.oidc.api.data.User;
import de.srsoftware.utils.Error;
import de.srsoftware.utils.Optionals; import de.srsoftware.utils.Optionals;
import java.io.IOException; import java.io.IOException;
import java.time.Instant; import java.time.Instant;
@ -32,13 +32,6 @@ public class ClientController extends Controller {
users = userService; users = userService;
} }
private boolean authorizationError(HttpExchange ex, String errorCode, String description, String state) throws IOException {
var map = new HashMap<String, String>();
map.put(ERROR, errorCode);
emptyIfBlank(description).ifPresent(d -> map.put(ERROR_DESCRIPTION, d));
emptyIfBlank(state).ifPresent(s -> map.put(STATE, s));
return badRequest(ex, map);
}
private boolean authorize(HttpExchange ex, Session session) throws IOException { private boolean authorize(HttpExchange ex, Session session) throws IOException {
var optUser = users.load(session.userId()); var optUser = users.load(session.userId());
@ -46,34 +39,30 @@ public class ClientController extends Controller {
var user = optUser.get(); var user = optUser.get();
var json = json(ex); var json = json(ex);
var state = json.has(STATE) ? json.getString(STATE) : null; var state = json.has(STATE) ? json.getString(STATE) : null;
if (!json.has(CLIENT_ID)) return authorizationError(ex, INVALID_REQUEST, "Missing required parameter \"%s\"!".formatted(CLIENT_ID), state); if (!json.has(CLIENT_ID)) return badRequest(ex, Error.message(ERROR_MISSING_PARAMETER, PARAM, CLIENT_ID, STATE, state));
var clientId = json.getString(CLIENT_ID); var clientId = json.getString(CLIENT_ID);
var optClient = clients.getClient(clientId); var optClient = clients.getClient(clientId);
if (optClient.isEmpty()) return authorizationError(ex, INVALID_REQUEST_OBJECT, "unknown client: %s".formatted(clientId), state); if (optClient.isEmpty()) return badRequest(ex, Error.message(ERROR_UNKNOWN_CLIENT, CLIENT_ID, clientId, STATE, state));
for (String param : List.of(SCOPE, RESPONSE_TYPE, REDIRECT_URI)) { for (String param : List.of(SCOPE, RESPONSE_TYPE, REDIRECT_URI)) {
if (!json.has(param)) return authorizationError(ex, INVALID_REQUEST, "Missing required parameter \"%s\"!".formatted(param), state); if (!json.has(param)) return badRequest(ex, Error.message(ERROR_MISSING_PARAMETER, PARAM, param, STATE, state));
} }
var scopes = toList(json, SCOPE); var scopes = toList(json, SCOPE);
if (!scopes.contains(OPENID)) return authorizationError(ex, INVALID_SCOPE, "This is an OpenID Provider. You should request \"openid\" scope!", 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);
for (var responseType : responseTypes) { for (var responseType : responseTypes) {
switch (responseType) { switch (responseType) {
case ID_TOKEN:
case TOKEN:
return authorizationError(ex, REQUEST_NOT_SUPPORTED, "Response type \"%s\" currently not supported".formatted(responseType), state);
case CODE: case CODE:
break; break;
default: default:
return authorizationError(ex, INVALID_REQUEST_OBJECT, "Unknown response type \"%s\"".formatted(responseType), state); return badRequest(ex, Error.message(ERROR_UNSUPPORTED_RESPONSE_TYPE, RESPONSE_TYPE, responseType, STATE, state));
} }
} }
if (!responseTypes.contains(CODE)) return authorizationError(ex, REQUEST_NOT_SUPPORTED, "Sorry, at the moment I can only handle \"%s\" response type".formatted(CODE), state); if (!responseTypes.contains(CODE)) return badRequest(ex, Error.message(ERROR_MISSONG_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);
if (!client.redirectUris().contains(redirect)) authorizationError(ex, INVALID_REDIRECT_URI, "unknown redirect uri: %s".formatted(redirect), state);
if (!client.redirectUris().contains(redirect)) return badRequest(ex, Error.message(ERROR_INVALID_REDIRECT, REDIRECT_URI, redirect, STATE, state));
if (json.has(AUTHORZED)) { // user did consent if (json.has(AUTHORZED)) { // user did consent
var authorized = json.getJSONObject(AUTHORZED); var authorized = json.getJSONObject(AUTHORZED);

16
de.srsoftware.oidc.web/src/main/resources/en/authorization.html

@ -22,7 +22,21 @@
<button type="button" onclick="grantAutorization(365)">Yes - for 1 year</button> <button type="button" onclick="grantAutorization(365)">Yes - for 1 year</button>
<button type="button" onclick="denyAutorization()">No</button> <button type="button" onclick="denyAutorization()">No</button>
</div> </div>
<div id="error" class="error" style="display: none"></div> <div id="error_missing_parameter" class="error">
Request does not contain required parameter "<span id="parameter"></span>"!
</div>
<div id="error_unknown_client" class="error">
Client "<span id="client_id"></span>" unknown to backend!
</div>
<div id="error_unsupported_response_type" class="error">
Response type "<span id="response_type"></span>" not supported!
</div>
<div id="error_missing_code" class="error">
Missing response type: code
</div>
<div id="error_invalid_redirect" class="error">
invalid redirect: <span id="redirect_uri"></span>
</div>
<div id="missing_scopes" class="error" style="display: none">Authorization resource contained neither list of <em>unauthorized scopes</em> nor list of <em>authorized scopes</em>! This is a server problem.</div> <div id="missing_scopes" class="error" style="display: none">Authorization resource contained neither list of <em>unauthorized scopes</em> nor list of <em>authorized scopes</em>! This is a server problem.</div>
</body> </body>
</html> </html>

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

@ -18,6 +18,7 @@ function showScope(response,scope){
} }
function handleResponse(response){ function handleResponse(response){
hideAll('error');
if (response.ok){ if (response.ok){
response.json().then(json => { response.json().then(json => {
if (json.rp) { if (json.rp) {
@ -43,19 +44,23 @@ function handleResponse(response){
}); });
} else { } else {
console.log("handleResponse(…) ← ",response); console.log("handleResponse(…) ← ",response);
if (response.status == 401){ if (response.status == 401){ // unauthorized
login(); login();
return; return;
} }
response.json().then(json => { response.json().then(json => {
setText('error',"Error: <br/>"+json.error_description); console.log("handleResponse → error",json);
show('error'); if (json.error) show(json.error);
if (json.metadata.client_id) setText('client_id',json.metadata.client_id);
if (json.metadata.parameter) setText('parameter',json.metadata.parameter);
if (json.metadata.redirect_uri) setText('redirect_uri',json.metadata.redirect_uri);
if (json.metadata.response_type)setText('response_type',json.metadata.response_type)
}); });
if (json.error != "invalid_request_uri"){ /*if (json.error != "invalid_request_uri"){
var url = params.get('redirect_uri') + '?' + new URLSearchParams(json).toString(); var url = params.get('redirect_uri') + '?' + new URLSearchParams(json).toString();
console.log('redirecting to '+url); console.log('redirecting to '+url);
redirect(url); redirect(url);
} }*/
} }
} }

Loading…
Cancel
Save