re-implemented first part: authorization

Signed-off-by: Stephan Richter <s.richter@srsoftware.de>
This commit is contained in:
2024-08-04 20:12:29 +02:00
parent 43b9b427c7
commit 5c7f86c4a4
22 changed files with 64 additions and 41 deletions

View File

@@ -12,18 +12,25 @@ public class Constants {
public static final String CLIENT_ID = "client_id";
public static final String CLIENT_SECRET = "client_secret";
public static final String CODE = "code";
public static final String ERROR = "error";
public static final String CONFIG_PATH = "LIGHTOIDC_CONFIG_PATH";
public static final String CONFIRMED = "confirmed";
public static final String DAYS = "days";
public static final String ERROR_DESCRIPTION = "error_description";
public static final String EXPIRATION = "expiration";
public static final String EXPIRES_IN = "expires_in";
public static final String GRANT_TYPE = "grant_type";
public static final String ID_TOKEN = "id_token";
public static final String INVALID_REDIRECT_URI = "invalid_request_uri";
public static final String INVALID_REQUEST = "invalid_request";
public static final String INVALID_REQUEST_OBJECT = "invalid_request_object";
public static final String INVALID_SCOPE = "invalid_scope";
public static final String NAME = "name";
public static final String NONCE = "nonce";
public static final String OPENID = "openid";
public static final String REDIRECT_URI = "redirect_uri";
public static final String REDIRECT_URIS = "redirect_uris";
public static final String REQUEST_NOT_SUPPORTED = "request_not_supported";
public static final String RESPONSE_TYPE = "response_type";
public static final String SCOPE = "scope";
public static final String SECRET = "secret";

View File

@@ -25,36 +25,45 @@ public class ClientController extends Controller {
clients = clientService;
}
private boolean authorizationError(HttpExchange ex, String errorCode, String description, String state) throws IOException {
var map = new HashMap<String, String>();
map.put(ERROR,errorCode);
if (description != null) map.put(ERROR_DESCRIPTION,description);
if (state != null) map.put(STATE,state);
return badRequest(ex,map);
}
private boolean authorize(HttpExchange ex, Session session) throws IOException {
var user = session.user();
var json = json(ex);
for (String param : List.of(SCOPE, RESPONSE_TYPE, CLIENT_ID, REDIRECT_URI)) {
if (!json.has(param)) return badRequest(ex, "Missing required parameter \"%s\"!".formatted(param));
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);
var clientId = json.getString(CLIENT_ID);
var optClient = clients.getClient(clientId);
if (optClient.isEmpty()) return authorizationError(ex,INVALID_REQUEST_OBJECT,"unknown client: %s".formatted(clientId),state);
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);
}
var scopes = toList(json, SCOPE);
if (!scopes.contains(OPENID)) return badRequest(ex, "This is an OpenID Provider. You should request \"openid\" scope!");
if (!scopes.contains(OPENID)) return authorizationError(ex,INVALID_SCOPE,"This is an OpenID Provider. You should request \"openid\" scope!",state);
var responseTypes = toList(json, RESPONSE_TYPE);
for (var responseType : responseTypes) {
switch (responseType) {
case ID_TOKEN:
case TOKEN:
return sendContent(ex, HTTP_NOT_IMPLEMENTED, "Response type \"%s\" currently not supported".formatted(responseType));
return authorizationError(ex, REQUEST_NOT_SUPPORTED, "Response type \"%s\" currently not supported".formatted(responseType),state);
case CODE:
break;
default:
return badRequest(ex, "Unknown response type \"%s\"".formatted(responseType));
return authorizationError(ex,INVALID_REQUEST_OBJECT,"Unknown response type \"%s\"".formatted(responseType),state);
}
}
if (!responseTypes.contains(CODE)) return badRequest(ex, "Sorry, at the moment I can only handle \"%s\" response type".formatted(CODE));
if ( !responseTypes.contains(CODE)) return authorizationError(ex, REQUEST_NOT_SUPPORTED, "Sorry, at the moment I can only handle \"%s\" response type".formatted(CODE),state);
var clientId = json.getString(CLIENT_ID);
var optClient = clients.getClient(clientId);
if (optClient.isEmpty()) return badRequest(ex, Map.of(CAUSE, "unknown client", CLIENT_ID, clientId));
var client = optClient.get();
var redirect = json.getString(REDIRECT_URI);
if (!client.redirectUris().contains(redirect)) return badRequest(ex, Map.of(CAUSE, "unknown redirect uri", REDIRECT_URI, redirect));
var state = json.has(STATE) ? json.getString(STATE) : null;
if (!client.redirectUris().contains(redirect)) authorizationError(ex, INVALID_REDIRECT_URI, "unknown redirect uri: %s".formatted(redirect),state);
client.nonce(json.has(NONCE) ? json.getString(NONCE) : null);
if (json.has(AUTHORZED)) {
@@ -69,9 +78,9 @@ public class ClientController extends Controller {
if (!authResult.unauthorizedScopes().isEmpty()) {
return sendContent(ex, Map.of("unauthorized_scopes", authResult.unauthorizedScopes(), "rp", client.name()));
}
var authoriedScopes = authResult.authorizedScopes().stream().map(AuthorizedScope::scope).collect(Collectors.joining(" "));
var authorizedScopes = authResult.authorizedScopes().stream().map(AuthorizedScope::scope).collect(Collectors.joining(" "));
var result = new HashMap<String, String>();
result.put(SCOPE, authoriedScopes);
result.put(SCOPE, authorizedScopes);
result.put(CODE, authResult.authCode());
if (state != null) result.put(STATE, state);
return sendContent(ex, result);

View File

@@ -2,9 +2,9 @@
<head>
<meta charset="utf-8">
<title>Light OIDC</title>
<script src="common.js"></script>
<script src="user.js"></script>
<script src="clients.js"></script>
<script src="scripts/common.js"></script>
<script src="scripts/user.js"></script>
<script src="scripts/clients.js"></script>
<link rel="stylesheet" href="style.css" />
</head>
<body>

View File

@@ -1,5 +1,6 @@
<a href="index.html">Übersicht</a>
<a href="clients.html" class="MANAGE_CLIENTS">Clients</a>
<a href="users.html" class="MANAGE_USERS">Benutzer</a>
<a href="https://openid.net/specs/openid-connect-core-1_0.html#CodeFlowAuth" target="_blank">Spec</a>
<a href="settings.html">Einstellungen</a>
<a href="logout.html">Ausloggen</a>

View File

@@ -2,9 +2,9 @@
<head>
<meta charset="utf-8">
<title>Light OIDC</title>
<script src="common.js"></script>
<script src="user.js"></script>
<script src="authorization.js"></script>
<script src="scripts/common.js"></script>
<script src="scripts/user.js"></script>
<script src="scripts/authorization.js"></script>
<link rel="stylesheet" href="style.css" />
</head>
<body>

View File

@@ -2,9 +2,9 @@
<head>
<meta charset="utf-8">
<title>Light OIDC</title>
<script src="common.js"></script>
<script src="user.js"></script>
<script src="clients.js"></script>
<script src="scripts/common.js"></script>
<script src="scripts/user.js"></script>
<script src="scripts/clients.js"></script>
<link rel="stylesheet" href="style.css" />
</head>
<body>

View File

@@ -2,9 +2,9 @@
<head>
<meta charset="utf-8">
<title>Light OIDC</title>
<script src="common.js"></script>
<script src="user.js"></script>
<script src="edit_client.js"></script>
<script src="scripts/common.js"></script>
<script src="scripts/user.js"></script>
<script src="scripts/edit_client.js"></script>
<link rel="stylesheet" href="style.css" />
</head>
<body>

View File

@@ -2,8 +2,8 @@
<head>
<meta charset="utf-8">
<title>Light OIDC</title>
<script src="common.js"></script>
<script src="user.js"></script>
<script src="scripts/common.js"></script>
<script src="scripts/user.js"></script>
<link rel="stylesheet" href="style.css" />
</head>
<body>

View File

@@ -2,8 +2,8 @@
<head>
<meta charset="utf-8">
<title>Light OIDC</title>
<script src="common.js"></script>
<script src="login.js"></script>
<script src="scripts/common.js"></script>
<script src="scripts/login.js"></script>
<link rel="stylesheet" href="style.css" />
</head>
<body>

View File

@@ -2,8 +2,8 @@
<head>
<meta charset="utf-8">
<title>Light OIDC</title>
<script src="common.js"></script>
<script src="logout.js"></script>
<script src="scripts/common.js"></script>
<script src="scripts/logout.js"></script>
<link rel="stylesheet" href="style.css" />
</head>
<body>

View File

@@ -1,5 +1,6 @@
<a href="index.html">Dashboard</a>
<a href="clients.html" class="MANAGE_CLIENTS">Clients</a>
<a href="users.html" class="MANAGE_USERS">Users</a>
<a href="https://openid.net/specs/openid-connect-core-1_0.html#CodeFlowAuth" target="_blank">Spec</a>
<a href="settings.html">Settings</a>
<a href="logout.html">Logout</a>

View File

@@ -2,9 +2,9 @@
<head>
<meta charset="utf-8">
<title>Light OIDC</title>
<script src="common.js"></script>
<script src="user.js"></script>
<script src="new_client.js"></script>
<script src="scripts/common.js"></script>
<script src="scripts/user.js"></script>
<script src="scripts/new_client.js"></script>
<link rel="stylesheet" href="style.css" />
</head>
<body>

View File

@@ -39,14 +39,19 @@ async function handleResponse(response){
}
show('missing_scopes');
} else {
console.log(response);
console.log("handleResponse(…) ← ",response);
if (response.status == 401){
login();
return;
}
var text = await response.text();
setText('error',"Error: <br/>"+text);
var json = await response.json();
setText('error',"Error: <br/>"+json.error_description);
show('error');
if (json.error != "invalid_request_uri"){
var url = params.get('redirect_uri') + '?' + new URLSearchParams(json).toString();
console.log('redirecting to '+url);
redirect(url);
}
}
}
@@ -56,7 +61,7 @@ function grantAutorization(days){
}
function denyAutorization(){
redirect(params.get('redirect_uri')+"?error=access denied");
redirect(params.get('redirect_uri')+"?error=consent_required");
}
function backendAutorization(){

View File

@@ -2,9 +2,9 @@
<head>
<meta charset="utf-8">
<title>Light OIDC</title>
<script src="common.js"></script>
<script src="user.js"></script>
<script src="settings.js"></script>
<script src="scripts/common.js"></script>
<script src="scripts/user.js"></script>
<script src="scripts/settings.js"></script>
<link rel="stylesheet" href="style.css" />
</head>
<body>