Gültigkeitsdauer von Tokens editierbar gemacht
Signed-off-by: Stephan Richter <s.richter@srsoftware.de>
This commit is contained in:
@@ -64,6 +64,7 @@ public class Constants {
|
|||||||
public static final String START_TLS = "start_tls";
|
public static final String START_TLS = "start_tls";
|
||||||
public static final String TOKEN = "token";
|
public static final String TOKEN = "token";
|
||||||
public static final String TOKEN_TYPE = "token_type";
|
public static final String TOKEN_TYPE = "token_type";
|
||||||
|
public static final String TOKEN_VALIDITY = "token_validity";
|
||||||
public static final String TRUST = "trust";
|
public static final String TRUST = "trust";
|
||||||
public static final String UNAUTHORIZED_CLIENT = "unauthorized_client";
|
public static final String UNAUTHORIZED_CLIENT = "unauthorized_client";
|
||||||
public static final String USER = "user";
|
public static final String USER = "user";
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ package de.srsoftware.oidc.api.data;
|
|||||||
import static de.srsoftware.oidc.api.Constants.*;
|
import static de.srsoftware.oidc.api.Constants.*;
|
||||||
import static de.srsoftware.utils.Optionals.nullable;
|
import static de.srsoftware.utils.Optionals.nullable;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
public final class Client {
|
public final class Client {
|
||||||
@@ -14,15 +15,36 @@ public final class Client {
|
|||||||
private final String name;
|
private final String name;
|
||||||
private final String secret;
|
private final String secret;
|
||||||
private final Set<String> redirectUris;
|
private final Set<String> redirectUris;
|
||||||
|
private Duration tokenValidity;
|
||||||
|
|
||||||
public Client(String id, String name, String secret, Set<String> redirectUris) {
|
public Client(String id, String name, String secret, Set<String> redirectUris) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
landingPage = null;
|
landingPage = null;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.secret = secret;
|
this.secret = secret;
|
||||||
this.redirectUris = redirectUris;
|
this.redirectUris = redirectUris;
|
||||||
|
this.tokenValidity = Duration.ofMinutes(10);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (obj == this) return true;
|
||||||
|
if (obj == null || obj.getClass() != this.getClass()) return false;
|
||||||
|
var that = (Client)obj;
|
||||||
|
return Objects.equals(this.id, that.id) //
|
||||||
|
&& Objects.equals(this.name, that.name) //
|
||||||
|
&& Objects.equals(this.secret, that.secret) //
|
||||||
|
&& Objects.equals(this.redirectUris, that.redirectUris) //
|
||||||
|
&& Objects.equals(landingPage, that.landingPage) //
|
||||||
|
&& Objects.equals(tokenValidity, that.tokenValidity);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(id, name, secret, redirectUris);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public String id() {
|
public String id() {
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
@@ -53,6 +75,7 @@ public final class Client {
|
|||||||
map.put(NAME, name);
|
map.put(NAME, name);
|
||||||
nullable(redirectUris).ifPresent(uris -> map.put(REDIRECT_URIS, uris));
|
nullable(redirectUris).ifPresent(uris -> map.put(REDIRECT_URIS, uris));
|
||||||
nullable(landingPage).ifPresent(lp -> map.put(LANDING_PAGE, lp));
|
nullable(landingPage).ifPresent(lp -> map.put(LANDING_PAGE, lp));
|
||||||
|
nullable(tokenValidity).ifPresent(tv -> map.put(TOKEN_VALIDITY, tv.toMinutes()));
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,17 +88,14 @@ public final class Client {
|
|||||||
return redirectUris;
|
return redirectUris;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object obj) {
|
public Duration tokenValidity() {
|
||||||
if (obj == this) return true;
|
return tokenValidity;
|
||||||
if (obj == null || obj.getClass() != this.getClass()) return false;
|
|
||||||
var that = (Client)obj;
|
|
||||||
return Objects.equals(this.id, that.id) && Objects.equals(this.name, that.name) && Objects.equals(this.secret, that.secret) && Objects.equals(this.redirectUris, that.redirectUris);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public Client tokenValidity(Duration newValue) {
|
||||||
public int hashCode() {
|
this.tokenValidity = newValue;
|
||||||
return Objects.hash(id, name, secret, redirectUris);
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
|
|||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
import de.srsoftware.oidc.api.data.Client;
|
import de.srsoftware.oidc.api.data.Client;
|
||||||
|
import java.time.Duration;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
@@ -23,7 +24,7 @@ public abstract class ClientServiceTest {
|
|||||||
var clientId = uuid();
|
var clientId = uuid();
|
||||||
var clientSecret = uuid();
|
var clientSecret = uuid();
|
||||||
var landingPage = uuid();
|
var landingPage = uuid();
|
||||||
var client = new Client(clientId, NAME, clientSecret, Set.of(URI)).landingPage(landingPage);
|
var client = new Client(clientId, NAME, clientSecret, Set.of(URI));
|
||||||
var list = cs.save(client).listClients();
|
var list = cs.save(client).listClients();
|
||||||
assertEquals(1, list.size());
|
assertEquals(1, list.size());
|
||||||
assertTrue(list.contains(client));
|
assertTrue(list.contains(client));
|
||||||
@@ -37,7 +38,7 @@ public abstract class ClientServiceTest {
|
|||||||
var clientId = uuid();
|
var clientId = uuid();
|
||||||
var clientSecret = uuid();
|
var clientSecret = uuid();
|
||||||
var landingPage = uuid();
|
var landingPage = uuid();
|
||||||
var client = new Client(clientId, NAME, clientSecret, Set.of(URI)).landingPage(landingPage);
|
var client = new Client(clientId, NAME, clientSecret, Set.of(URI)).landingPage(landingPage).tokenValidity(Duration.ofMinutes(23));
|
||||||
var optClient = cs.save(client).getClient(clientId);
|
var optClient = cs.save(client).getClient(clientId);
|
||||||
assertTrue(optClient.isPresent());
|
assertTrue(optClient.isPresent());
|
||||||
assertEquals(client, optClient.get());
|
assertEquals(client, optClient.get());
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import de.srsoftware.oidc.api.data.User;
|
|||||||
import de.srsoftware.utils.Error;
|
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.Duration;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.time.temporal.ChronoUnit;
|
import java.time.temporal.ChronoUnit;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
@@ -210,7 +211,8 @@ public class ClientController extends Controller {
|
|||||||
if (o instanceof String s) redirects.add(s);
|
if (o instanceof String s) redirects.add(s);
|
||||||
}
|
}
|
||||||
var landingPage = json.has(LANDING_PAGE) ? json.getString(LANDING_PAGE) : null;
|
var landingPage = json.has(LANDING_PAGE) ? json.getString(LANDING_PAGE) : null;
|
||||||
var client = new Client(json.getString(CLIENT_ID), json.getString(NAME), json.getString(SECRET), redirects).landingPage(landingPage);
|
var token_duration = Duration.ofMinutes(json.has(TOKEN_VALIDITY) ? json.getLong(TOKEN_VALIDITY) : 10);
|
||||||
|
var client = new Client(json.getString(CLIENT_ID), json.getString(NAME), json.getString(SECRET), redirects).landingPage(landingPage).tokenValidity(token_duration);
|
||||||
clients.save(client);
|
clients.save(client);
|
||||||
return sendContent(ex, client);
|
return sendContent(ex, client);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -176,7 +176,7 @@ public class TokenController extends PathHandler {
|
|||||||
claims.setIssuer(issuer); // who creates the token and signs it
|
claims.setIssuer(issuer); // who creates the token and signs it
|
||||||
claims.setSubject(user.uuid()); // the subject/principal is whom the token is about
|
claims.setSubject(user.uuid()); // the subject/principal is whom the token is about
|
||||||
claims.setAudience(client.id());
|
claims.setAudience(client.id());
|
||||||
claims.setExpirationTimeMinutesInTheFuture(config.tokenExpirationMinutes); // time when the token will expire (10 minutes from now)
|
claims.setExpirationTimeMinutesInTheFuture(client.tokenValidity().toMinutes()); // time when the token will expire (10 minutes from now)
|
||||||
claims.setIssuedAtToNow();
|
claims.setIssuedAtToNow();
|
||||||
claims.setClaim(AT_HASH, atHash);
|
claims.setClaim(AT_HASH, atHash);
|
||||||
claims.setClaim(CLIENT_ID, client.id());
|
claims.setClaim(CLIENT_ID, client.id());
|
||||||
|
|||||||
@@ -19,12 +19,12 @@ public class EncryptedClientService extends EncryptedConfig implements ClientSer
|
|||||||
|
|
||||||
public Client decrypt(Client client) {
|
public Client decrypt(Client client) {
|
||||||
var decryptedUrls = client.redirectUris().stream().map(this::decrypt).collect(Collectors.toSet());
|
var decryptedUrls = client.redirectUris().stream().map(this::decrypt).collect(Collectors.toSet());
|
||||||
return new Client(decrypt(client.id()), decrypt(client.name()), decrypt(client.secret()), decryptedUrls).landingPage(decrypt(client.landingPage()));
|
return new Client(decrypt(client.id()), decrypt(client.name()), decrypt(client.secret()), decryptedUrls).landingPage(decrypt(client.landingPage())).tokenValidity(client.tokenValidity());
|
||||||
}
|
}
|
||||||
|
|
||||||
public Client encrypt(Client client) {
|
public Client encrypt(Client client) {
|
||||||
var encryptedUrls = client.redirectUris().stream().map(this::encrypt).collect(Collectors.toSet());
|
var encryptedUrls = client.redirectUris().stream().map(this::encrypt).collect(Collectors.toSet());
|
||||||
return new Client(encrypt(client.id()), encrypt(client.name()), encrypt(client.secret()), encryptedUrls).landingPage(encrypt(client.landingPage()));
|
return new Client(encrypt(client.id()), encrypt(client.name()), encrypt(client.secret()), encryptedUrls).landingPage(encrypt(client.landingPage())).tokenValidity(client.tokenValidity());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import java.io.FileNotFoundException;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
import java.time.Duration;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.time.temporal.ChronoUnit;
|
import java.time.temporal.ChronoUnit;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
@@ -329,6 +330,7 @@ public class FileStore implements AuthorizationService, ClientService, SessionSe
|
|||||||
}
|
}
|
||||||
var client = new Client(clientId, clientData.getString(NAME), clientData.getString(SECRET), redirectUris);
|
var client = new Client(clientId, clientData.getString(NAME), clientData.getString(SECRET), redirectUris);
|
||||||
if (clientData.has(LANDING_PAGE)) client.landingPage(clientData.getString(LANDING_PAGE));
|
if (clientData.has(LANDING_PAGE)) client.landingPage(clientData.getString(LANDING_PAGE));
|
||||||
|
if (clientData.has(TOKEN_VALIDITY)) client.tokenValidity(Duration.ofMinutes(clientData.getLong(TOKEN_VALIDITY)));
|
||||||
return client;
|
return client;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -37,6 +37,14 @@
|
|||||||
<th>Ziel-Seite</th>
|
<th>Ziel-Seite</th>
|
||||||
<td><input type="text" id="landing-page" /></td>
|
<td><input type="text" id="landing-page" /></td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Gültigkeitsdauer von Access-Tokens</th>
|
||||||
|
<td>
|
||||||
|
<input type="range" id="token_validity" min="1" max="120" oninput="durationUpdate()" />
|
||||||
|
<br/>
|
||||||
|
<span id="days"></span> Tage, <span id="hours"></span> Stunden, <span id="minutes"></span> Minuten
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td></td>
|
<td></td>
|
||||||
<td><button type="button" id="button" onclick="updateClient();">Aktualisieren</button></td>
|
<td><button type="button" id="button" onclick="updateClient();">Aktualisieren</button></td>
|
||||||
|
|||||||
@@ -37,10 +37,19 @@
|
|||||||
<th>Landing page</th>
|
<th>Landing page</th>
|
||||||
<td><input type="text" id="landing-page" /></td>
|
<td><input type="text" id="landing-page" /></td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Token validity duration</th>
|
||||||
|
<td>
|
||||||
|
<input type="range" id="token_validity" min="1" max="120" oninput="durationUpdate()" />
|
||||||
|
<br/>
|
||||||
|
<span id="days"></span> days, <span id="hours"></span> hours, <span id="minutes"></span> minutes
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td></td>
|
<td></td>
|
||||||
<td><button type="button" id="button" onclick="updateClient();">Update</button></td>
|
<td><button type="button" id="button" onclick="updateClient();">Update</button></td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
</table>
|
</table>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<fieldset class="wide">
|
<fieldset class="wide">
|
||||||
|
|||||||
@@ -1,5 +1,22 @@
|
|||||||
var params = new URLSearchParams(window.location.search);
|
var params = new URLSearchParams(window.location.search);
|
||||||
var id = params.get('id');
|
var id = params.get('id');
|
||||||
|
var token_validity = 10;
|
||||||
|
|
||||||
|
function displayDuration(){
|
||||||
|
var mins = token_validity;
|
||||||
|
hrs = Math.floor(mins/60);
|
||||||
|
mins-=60*hrs;
|
||||||
|
days = Math.floor(hrs/24);
|
||||||
|
hrs-=24*days;
|
||||||
|
setText('days',days);
|
||||||
|
setText('hours',hrs);
|
||||||
|
setText('minutes',mins);
|
||||||
|
}
|
||||||
|
|
||||||
|
function durationUpdate(){
|
||||||
|
token_validity = getValue('token_validity');
|
||||||
|
displayDuration();
|
||||||
|
}
|
||||||
|
|
||||||
function handleAutoDiscover(response){
|
function handleAutoDiscover(response){
|
||||||
if (response.ok){
|
if (response.ok){
|
||||||
@@ -19,6 +36,8 @@ function handleLoadResponse(response){
|
|||||||
get('client-secret').value = json.secret;
|
get('client-secret').value = json.secret;
|
||||||
get('redirect-urls').value = json.redirect_uris.join("\n");
|
get('redirect-urls').value = json.redirect_uris.join("\n");
|
||||||
get('landing-page').value = json.landing_page?json.landing_page:'';
|
get('landing-page').value = json.landing_page?json.landing_page:'';
|
||||||
|
token_validity = json.token_validity?json.token_validity:10;
|
||||||
|
displayDuration();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -44,7 +63,8 @@ function updateClient(){
|
|||||||
name : getValue('client-name'),
|
name : getValue('client-name'),
|
||||||
secret : getValue('client-secret'),
|
secret : getValue('client-secret'),
|
||||||
redirect_uris : getValue('redirect-urls').split("\n"),
|
redirect_uris : getValue('redirect-urls').split("\n"),
|
||||||
landing_page : getValue('landing-page')
|
landing_page : getValue('landing-page'),
|
||||||
|
token_validity : getValue('token_validity')
|
||||||
};
|
};
|
||||||
fetch(client_controller+'/update',{
|
fetch(client_controller+'/update',{
|
||||||
method : 'POST',
|
method : 'POST',
|
||||||
|
|||||||
@@ -16,6 +16,8 @@
|
|||||||
<li>implement token refresh</li>
|
<li>implement token refresh</li>
|
||||||
<li>Configuration im Frontend</li>
|
<li>Configuration im Frontend</li>
|
||||||
<li>TOTP authentifizierung</li>
|
<li>TOTP authentifizierung</li>
|
||||||
|
<li>Gültigkeitsdauer von Tokens pro Client konfigurierbar machen</li>
|
||||||
|
<li>Besserer Hinweis beim Zurücksetzen von Passworten, wenn das neue Passwort zu einfach ist</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
Reference in New Issue
Block a user