diff --git a/de.srsoftware.oidc.api/src/main/java/de/srsoftware/oidc/api/AuthorizationService.java b/de.srsoftware.oidc.api/src/main/java/de/srsoftware/oidc/api/AuthorizationService.java deleted file mode 100644 index c9e4022..0000000 --- a/de.srsoftware.oidc.api/src/main/java/de/srsoftware/oidc/api/AuthorizationService.java +++ /dev/null @@ -1,17 +0,0 @@ -/* © SRSoftware 2024 */ -package de.srsoftware.oidc.api; - -import java.time.Instant; -import java.util.List; -import java.util.Optional; - -public interface AuthorizationService { - AuthorizationService addCode(Client client, User user, String code); - AuthorizationService authorize(Client client, User user, Instant expiration); - boolean isAuthorized(Client client, User user); - List authorizedUsers(Client client); - List authorizedClients(User user); - AuthorizationService revoke(Client client, User user); - - Optional forCode(String code); -} diff --git a/de.srsoftware.oidc.api/src/main/java/de/srsoftware/oidc/api/Authorization.java b/de.srsoftware.oidc.api/src/main/java/de/srsoftware/oidc/api/AuthorizedScope.java similarity index 51% rename from de.srsoftware.oidc.api/src/main/java/de/srsoftware/oidc/api/Authorization.java rename to de.srsoftware.oidc.api/src/main/java/de/srsoftware/oidc/api/AuthorizedScope.java index d766c20..42b20c5 100644 --- a/de.srsoftware.oidc.api/src/main/java/de/srsoftware/oidc/api/Authorization.java +++ b/de.srsoftware.oidc.api/src/main/java/de/srsoftware/oidc/api/AuthorizedScope.java @@ -3,5 +3,5 @@ package de.srsoftware.oidc.api; import java.time.Instant; -public record Authorization(String clientId, String userId, Instant expiration) { +public record AuthorizedScope(String scope, Instant expiration) { } diff --git a/de.srsoftware.oidc.api/src/main/java/de/srsoftware/oidc/api/ClaimAuthorizationService.java b/de.srsoftware.oidc.api/src/main/java/de/srsoftware/oidc/api/ClaimAuthorizationService.java new file mode 100644 index 0000000..f68c916 --- /dev/null +++ b/de.srsoftware.oidc.api/src/main/java/de/srsoftware/oidc/api/ClaimAuthorizationService.java @@ -0,0 +1,14 @@ +/* © SRSoftware 2024 */ +package de.srsoftware.oidc.api; + +import java.time.Instant; +import java.util.Collection; +import java.util.List; +import java.util.Set; + +public interface ClaimAuthorizationService { + public record AuthResult(List authorizedScopes, Set unauthorizedScopes, String authCode) { + } + AuthResult getAuthorization(User user, Client client, Collection scopes); + ClaimAuthorizationService authorize(User user, Client client, Collection scopes, Instant expiration); +} diff --git a/de.srsoftware.oidc.api/src/main/java/de/srsoftware/oidc/api/Constants.java b/de.srsoftware.oidc.api/src/main/java/de/srsoftware/oidc/api/Constants.java index 79054eb..521b10a 100644 --- a/de.srsoftware.oidc.api/src/main/java/de/srsoftware/oidc/api/Constants.java +++ b/de.srsoftware.oidc.api/src/main/java/de/srsoftware/oidc/api/Constants.java @@ -6,6 +6,7 @@ public class Constants { public static final String APP_NAME = "LightOIDC"; public static final String AUTH_CODE = "authorization_code"; public static final String AUTHORIZATION = "Authorization"; + public static final String AUTHORZED = "authorized"; public static final String BEARER = "Bearer"; public static final String CAUSE = "cause"; public static final String CLIENT_ID = "client_id"; @@ -14,6 +15,7 @@ public class Constants { 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 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"; diff --git a/de.srsoftware.oidc.backend/src/main/java/de/srsoftware/oidc/backend/ClientController.java b/de.srsoftware.oidc.backend/src/main/java/de/srsoftware/oidc/backend/ClientController.java index e79f78e..a73b237 100644 --- a/de.srsoftware.oidc.backend/src/main/java/de/srsoftware/oidc/backend/ClientController.java +++ b/de.srsoftware.oidc.backend/src/main/java/de/srsoftware/oidc/backend/ClientController.java @@ -3,26 +3,23 @@ package de.srsoftware.oidc.backend; import static de.srsoftware.oidc.api.Constants.*; import static de.srsoftware.oidc.api.Permission.MANAGE_CLIENTS; -import static de.srsoftware.utils.Strings.uuid; -import static java.lang.System.Logger.Level.ERROR; import static java.net.HttpURLConnection.*; import com.sun.net.httpserver.HttpExchange; import de.srsoftware.oidc.api.*; import java.io.IOException; -import java.time.Duration; import java.time.Instant; -import java.util.Arrays; -import java.util.HashSet; -import java.util.Map; +import java.time.temporal.ChronoUnit; +import java.util.*; +import java.util.stream.Collectors; import org.json.JSONObject; public class ClientController extends Controller { - private static final System.Logger LOG = System.getLogger(ClientController.class.getSimpleName()); - private final AuthorizationService authorizations; - private final ClientService clients; + private static final System.Logger LOG = System.getLogger(ClientController.class.getSimpleName()); + private final ClaimAuthorizationService authorizations; + private final ClientService clients; - public ClientController(AuthorizationService authorizationService, ClientService clientService, SessionService sessionService) { + public ClientController(ClaimAuthorizationService authorizationService, ClientService clientService, SessionService sessionService) { super(sessionService); authorizations = authorizationService; clients = clientService; @@ -30,33 +27,58 @@ public class ClientController extends Controller { private boolean authorize(HttpExchange ex, Session session) throws IOException { - var user = session.user(); - var json = json(ex); - var scope = json.getString(SCOPE); - if (!Arrays.asList(scope.split(" ")).contains(OPENID)) return sendContent(ex, HTTP_BAD_REQUEST, Map.of(ERROR, "openid scope missing in request")); + 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 scopes = toList(json, SCOPE); + if (!scopes.contains(OPENID)) return badRequest(ex, "This is an OpenID Provider. You should request \"openid\" scope!"); + 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)); + case CODE: + break; + default: + return badRequest(ex, "Unknown response type \"%s\"".formatted(responseType)); + } + } + if (!responseTypes.contains(CODE)) return badRequest(ex, "Sorry, at the moment I can only handle \"%s\" response type".formatted(CODE)); var clientId = json.getString(CLIENT_ID); - var redirect = json.getString(REDIRECT_URI); var optClient = clients.getClient(clientId); if (optClient.isEmpty()) return badRequest(ex, Map.of(CAUSE, "unknown client", CLIENT_ID, clientId)); - var client = optClient.get(); + 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; client.nonce(json.has(NONCE) ? json.getString(NONCE) : null); + if (json.has(AUTHORZED)) { + var authorized = json.getJSONObject(AUTHORZED); + var days = authorized.getInt("days"); + var list = new ArrayList(); + authorized.getJSONArray("scopes").forEach(scope -> list.add(scope.toString())); + authorizations.authorize(user, client, list, Instant.now().plus(days, ChronoUnit.DAYS)); + } - if (!authorizations.isAuthorized(client, session.user())) { - if (json.has(DAYS)) { - var days = json.getInt(DAYS); - var expiration = Instant.now().plus(Duration.ofDays(days)); - authorizations.authorize(client, user, expiration); - } else { - return sendContent(ex, Map.of(CONFIRMED, false, NAME, client.name())); - } + var authResult = authorizations.getAuthorization(user, client, scopes); + if (!authResult.unauthorizedScopes().isEmpty()) { + return sendContent(ex, Map.of("unauthorized_scopes", authResult.unauthorizedScopes(), "rp", client.name())); } - var state = json.getString(STATE); - var code = uuid(); - authorizations.addCode(client, session.user(), code); - return sendContent(ex, Map.of(CONFIRMED, true, CODE, code, REDIRECT_URI, redirect, STATE, state)); + var authoriedScopes = authResult.authorizedScopes().stream().map(AuthorizedScope::scope).collect(Collectors.joining(" ")); + var result = new HashMap(); + result.put(SCOPE, authoriedScopes); + result.put(CODE, authResult.authCode()); + if (state != null) result.put(STATE, state); + return sendContent(ex, result); + } + + private List toList(JSONObject json, String key) { + return Arrays.asList(json.getString(key).split(" ")); } private boolean deleteClient(HttpExchange ex, Session session) throws IOException { @@ -86,7 +108,7 @@ public class ClientController extends Controller { @Override public boolean doPost(String path, HttpExchange ex) throws IOException { var optSession = getSession(ex); - if (optSession.isEmpty()) return sendEmptyResponse(HTTP_UNAUTHORIZED, ex); + if (optSession.isEmpty()) return sendContent(ex, HTTP_UNAUTHORIZED, "No authorized!"); // post-login paths var session = optSession.get(); diff --git a/de.srsoftware.oidc.backend/src/main/java/de/srsoftware/oidc/backend/TokenController.java b/de.srsoftware.oidc.backend/src/main/java/de/srsoftware/oidc/backend/TokenController.java index 6243814..6180a9d 100644 --- a/de.srsoftware.oidc.backend/src/main/java/de/srsoftware/oidc/backend/TokenController.java +++ b/de.srsoftware.oidc.backend/src/main/java/de/srsoftware/oidc/backend/TokenController.java @@ -2,33 +2,29 @@ package de.srsoftware.oidc.backend; import static de.srsoftware.oidc.api.Constants.*; -import static de.srsoftware.utils.Optionals.nullable; import static java.lang.System.Logger.Level.*; -import static java.net.HttpURLConnection.HTTP_BAD_REQUEST; +import static java.net.HttpURLConnection.HTTP_NOT_IMPLEMENTED; import com.sun.net.httpserver.HttpExchange; import de.srsoftware.oidc.api.*; import java.io.IOException; -import java.net.URLDecoder; -import java.nio.charset.StandardCharsets; import java.util.*; import java.util.stream.Collectors; import org.jose4j.jwk.PublicJsonWebKey; import org.jose4j.jws.JsonWebSignature; import org.jose4j.jwt.JwtClaims; import org.jose4j.lang.JoseException; -import org.json.JSONObject; public class TokenController extends PathHandler { public record Configuration(String issuer, int tokenExpirationMinutes) { } - private final ClientService clients; - private final AuthorizationService authorizations; - private final UserService users; - private final KeyManager keyManager; - private Configuration config; + private final ClientService clients; + private final ClaimAuthorizationService authorizations; + private final UserService users; + private final KeyManager keyManager; + private Configuration config; - public TokenController(AuthorizationService authorizationService, ClientService clientService, KeyManager keyManager, UserService userService, Configuration configuration) { + public TokenController(ClaimAuthorizationService authorizationService, ClientService clientService, KeyManager keyManager, UserService userService, Configuration configuration) { authorizations = authorizationService; clients = clientService; this.keyManager = keyManager; @@ -53,7 +49,8 @@ public class TokenController extends PathHandler { private boolean provideToken(HttpExchange ex) throws IOException { var map = deserialize(body(ex)); // TODO: check data, → https://openid.net/specs/openid-connect-core-1_0.html#TokenEndpoint - + return sendEmptyResponse(HTTP_NOT_IMPLEMENTED, ex); + /* var grantType = map.get(GRANT_TYPE); if (!AUTH_CODE.equals(grantType)) return sendContent(ex, HTTP_BAD_REQUEST, Map.of(ERROR, "unknown grant type", GRANT_TYPE, grantType)); @@ -87,7 +84,7 @@ public class TokenController extends PathHandler { response.put(EXPIRES_IN, 3600); response.put(ID_TOKEN, jwToken); LOG.log(DEBUG, jwToken); - return sendContent(ex, response); + return sendContent(ex, response);*/ } private String createJWT(Client client, User user) { @@ -120,12 +117,12 @@ public class TokenController extends PathHandler { claims.setSubject(user.uuid()); // the subject/principal is whom the token is about claims.setAudience(client.id()); claims.setExpirationTimeMinutesInTheFuture(config.tokenExpirationMinutes); // time when the token will expire (10 minutes from now) - claims.setIssuedAtToNow(); // when the token was issued/created (now) + claims.setIssuedAtToNow(); // when the token was issued/created (now) claims.setClaim("client_id", client.id()); claims.setClaim("email", user.email()); // additional claims/attributes about the subject can be added client.nonce().ifPresent(nonce -> claims.setClaim(NONCE, nonce)); - claims.setGeneratedJwtId(); // a unique identifier for the token + claims.setGeneratedJwtId(); // a unique identifier for the token return claims; } } diff --git a/de.srsoftware.oidc.datastore.file/src/main/java/de/srsoftware/oidc/datastore/file/FileStore.java b/de.srsoftware.oidc.datastore.file/src/main/java/de/srsoftware/oidc/datastore/file/FileStore.java index d41b348..4304045 100644 --- a/de.srsoftware.oidc.datastore.file/src/main/java/de/srsoftware/oidc/datastore/file/FileStore.java +++ b/de.srsoftware.oidc.datastore.file/src/main/java/de/srsoftware/oidc/datastore/file/FileStore.java @@ -1,8 +1,10 @@ /* © SRSoftware 2024 */ package de.srsoftware.oidc.datastore.file; /* © SRSoftware 2024 */ +import static de.srsoftware.oidc.api.Constants.EXPIRATION; import static de.srsoftware.oidc.api.User.*; import static de.srsoftware.utils.Optionals.nullable; import static de.srsoftware.utils.Strings.uuid; +import static java.lang.System.Logger.Level.WARNING; import static java.util.Optional.empty; import de.srsoftware.oidc.api.*; @@ -17,17 +19,17 @@ import java.time.temporal.ChronoUnit; import java.util.*; import org.json.JSONObject; -public class FileStore implements AuthorizationService, ClientService, SessionService, UserService { - private static final String AUTHORIZATIONS = "authorizations"; - private static final String CLIENTS = "clients"; - private static final String CODES = "codes"; - private static final String EXPIRATION = "expiration"; - private static final String NAME = "name"; - private static final String REDIRECT_URIS = "redirect_uris"; - private static final String SECRET = "secret"; - private static final String SESSIONS = "sessions"; - private static final String USERS = "users"; - private static final String USER = "user"; +public class FileStore implements ClaimAuthorizationService, ClientService, SessionService, UserService { + private static final String AUTHORIZATIONS = "authorizations"; + private static final String CLIENTS = "clients"; + private static final String CODES = "codes"; + private static final System.Logger LOG = System.getLogger(FileStore.class.getSimpleName()); + private static final String NAME = "name"; + private static final String REDIRECT_URIS = "redirect_uris"; + private static final String SECRET = "secret"; + private static final String SESSIONS = "sessions"; + private static final String USERS = "users"; + private static final String USER = "user"; private final Path storageFile; private final JSONObject json; @@ -35,6 +37,7 @@ public class FileStore implements AuthorizationService, ClientService, SessionSe private Duration sessionDuration = Duration.of(10, ChronoUnit.MINUTES); private Map clients = new HashMap<>(); private Map accessTokens = new HashMap<>(); + private Map authCodes = new HashMap<>(); public FileStore(File storage, PasswordHasher passwordHasher) throws IOException { this.storageFile = storage.toPath(); @@ -262,80 +265,51 @@ public class FileStore implements AuthorizationService, ClientService, SessionSe return new Client(clientId, clientData.getString(NAME), clientData.getString(SECRET), redirectUris); } - - /*** Authorization service methods ***/ - - @Override - public Optional forCode(String code) { - var authorizations = json.getJSONObject(AUTHORIZATIONS); - if (!authorizations.has(code)) return empty(); - String authId = authorizations.getString(code); - if (!authorizations.has(authId)) { - authorizations.remove(code); - return empty(); - } - try { - var expiration = Instant.ofEpochSecond(authorizations.getLong(authId)); - if (expiration.isAfter(Instant.now())) { - String[] parts = authId.split("@"); - return Optional.of(new Authorization(parts[1], parts[0], expiration)); - } - authorizations.remove(authId); - } catch (Exception ignored) { - } - - return empty(); - } + /*** ClaimAuthorizationService methods ***/ @Override - public AuthorizationService addCode(Client client, User user, String code) { + public ClaimAuthorizationService authorize(User user, Client client, Collection scopes, Instant expiration) { + LOG.log(WARNING, "{0}.authorize({1}, {2}, {3}, {4}) not implemented", getClass().getSimpleName(), user.realName(), client.name(), scopes, expiration); var authorizations = json.getJSONObject(AUTHORIZATIONS); - authorizations.put(code, authorizationId(user, client)); + if (!authorizations.has(user.uuid())) authorizations.put(user.uuid(), new JSONObject()); + var userAuthorizations = authorizations.getJSONObject(user.uuid()); + if (!userAuthorizations.has(client.id())) userAuthorizations.put(client.id(), new JSONObject()); + var clientScopes = userAuthorizations.getJSONObject(client.id()); + for (var scope : scopes) clientScopes.put(scope, expiration.getEpochSecond()); save(); return this; } - @Override - public AuthorizationService authorize(Client client, User user, Instant expiration) { - var authorizations = json.getJSONObject(AUTHORIZATIONS); - authorizations.put(authorizationId(user, client), expiration.getEpochSecond()); - return this; - } - - private String authorizationId(User user, Client client) { - return String.join("@", user.uuid(), client.id()); + private AuthResult unauthorized(Collection scopes) { + return new AuthResult(List.of(), new HashSet<>(scopes), null); } @Override - public boolean isAuthorized(Client client, User user) { - var authorizations = json.getJSONObject(AUTHORIZATIONS); - var authId = authorizationId(user, client); - if (!authorizations.has(authId)) return false; - - try { - if (Instant.ofEpochSecond(authorizations.getLong(authId)).isAfter(Instant.now())) return true; - } catch (Exception ignored) { + public AuthResult getAuthorization(User user, Client client, Collection scopes) { + var authorizations = json.getJSONObject(AUTHORIZATIONS); + var userAuthorizations = authorizations.has(user.uuid()) ? authorizations.getJSONObject(user.uuid()) : null; + if (userAuthorizations == null) return unauthorized(scopes); + var clientScopes = userAuthorizations.has(client.id()) ? userAuthorizations.getJSONObject(client.id()) : null; + if (clientScopes == null) return unauthorized(scopes); + var now = Instant.now(); + var authorizedScopes = new ArrayList(); + var unauthorizedScopes = new HashSet(); + for (var scope : scopes) { + if (clientScopes.has(scope)) { + var expiration = Instant.ofEpochSecond(clientScopes.getLong(scope)); + if (expiration.isAfter(now)) { + authorizedScopes.add(new AuthorizedScope(scope, expiration)); + } else { + unauthorizedScopes.add(scope); + } + } } - revoke(client, user); - return false; - } - - @Override - public List authorizedUsers(Client client) { - return List.of(); + return new AuthResult(authorizedScopes, unauthorizedScopes, authCode(user, client)); } - @Override - public List authorizedClients(User user) { - return List.of(); - } - - @Override - public AuthorizationService revoke(Client client, User user) { - var authorizations = json.getJSONObject(AUTHORIZATIONS); - var authId = authorizationId(user, client); - if (!authorizations.has(authId)) return this; - authorizations.remove(authId); - return save(); + private String authCode(User user, Client client) { + var code = uuid(); + authCodes.put(user.uuid() + "@" + client.id(), code); + return code; } } diff --git a/de.srsoftware.oidc.web/src/main/resources/en/authorization.html b/de.srsoftware.oidc.web/src/main/resources/en/authorization.html index 109988a..3ca0571 100644 --- a/de.srsoftware.oidc.web/src/main/resources/en/authorization.html +++ b/de.srsoftware.oidc.web/src/main/resources/en/authorization.html @@ -10,14 +10,18 @@ + \ No newline at end of file diff --git a/de.srsoftware.oidc.web/src/main/resources/en/authorization.js b/de.srsoftware.oidc.web/src/main/resources/en/authorization.js index 40689df..b319459 100644 --- a/de.srsoftware.oidc.web/src/main/resources/en/authorization.js +++ b/de.srsoftware.oidc.web/src/main/resources/en/authorization.js @@ -1,32 +1,57 @@ var params = new URLSearchParams(window.location.search) var json = Object.fromEntries(params); +var scopes = {}; function showConfirmationDialog(name){ get('name').innerHTML = name; show('content'); } +async function showScope(response,scope){ + if (response.ok){ + var content = await response.text(); + get('scopes').innerHTML += content; + } else { + get('scopes').innerHTML += '
  • '+scope+' (???)
  • '; + } +} + async function handleResponse(response){ if (response.ok){ var json = await response.json(); - console.log("handleResponse(ok) ←",json); - if (!json.confirmed){ - showConfirmationDialog(json.name); - } else { - console.log('redirecting to '+json.redirect_uri+'?code='+json.code+'&state='+json.state+'&scope=openid'); - redirect(json.redirect_uri+'?code='+json.code+'&state='+json.state+'&scope=openid'); + if (json.rp) { + setText("rp",json.rp); + setText("rp2",json.rp); } - return; + get('scopes').innerHTML = ''; + if (json.unauthorized_scopes){ + scopes = json.unauthorized_scopes; + for (var scope of json.unauthorized_scopes){ + fetch(web+"scopes/"+scope+".html").then(response => showScope(response,scope)) + } + show("content"); + return; + } + if (json.scope){ + var url = params.get('redirect_uri') + '?' + new URLSearchParams(json).toString(); + redirect(url); + return; + } + show('missing_scopes'); } else { - var json = await response.json(); - console.log("handleResponse(error) ←",json); - get('error').innerHTML = "Error:
    "+JSON.stringify(json); + console.log(response); + if (response.status == 401){ + login(); + return; + } + var text = await response.text(); + setText('error',"Error:
    "+text); show('error'); } } function grantAutorization(days){ - json.days = days; + json['authorized'] = { days : days, scopes : scopes}; backendAutorization(); } diff --git a/de.srsoftware.oidc.web/src/main/resources/en/common.js b/de.srsoftware.oidc.web/src/main/resources/en/common.js index f0d8718..b212fd4 100644 --- a/de.srsoftware.oidc.web/src/main/resources/en/common.js +++ b/de.srsoftware.oidc.web/src/main/resources/en/common.js @@ -25,6 +25,10 @@ function hide(id){ get(id).style.display = 'none'; } +function login(){ + redirect('login.html?return_to='+encodeURIComponent(window.location.href)); +} + function redirect(page){ window.location.href = page; } diff --git a/de.srsoftware.oidc.web/src/main/resources/en/scopes/email.html b/de.srsoftware.oidc.web/src/main/resources/en/scopes/email.html new file mode 100644 index 0000000..901f722 --- /dev/null +++ b/de.srsoftware.oidc.web/src/main/resources/en/scopes/email.html @@ -0,0 +1 @@ +
  • Your email address
  • \ No newline at end of file diff --git a/de.srsoftware.oidc.web/src/main/resources/en/scopes/openid.html b/de.srsoftware.oidc.web/src/main/resources/en/scopes/openid.html new file mode 100644 index 0000000..12a8103 --- /dev/null +++ b/de.srsoftware.oidc.web/src/main/resources/en/scopes/openid.html @@ -0,0 +1 @@ +
  • Your OpenID – let the relying party know who you are
  • \ No newline at end of file diff --git a/de.srsoftware.oidc.web/src/main/resources/en/style.css b/de.srsoftware.oidc.web/src/main/resources/en/style.css index a97ff66..f0ba8c8 100644 --- a/de.srsoftware.oidc.web/src/main/resources/en/style.css +++ b/de.srsoftware.oidc.web/src/main/resources/en/style.css @@ -32,4 +32,8 @@ form th{ .warning{ color: yellow; +} + +.error{ + background-color: red; } \ No newline at end of file diff --git a/de.srsoftware.oidc.web/src/main/resources/en/user.js b/de.srsoftware.oidc.web/src/main/resources/en/user.js index 63c80ef..049522e 100644 --- a/de.srsoftware.oidc.web/src/main/resources/en/user.js +++ b/de.srsoftware.oidc.web/src/main/resources/en/user.js @@ -1,7 +1,7 @@ var user = null; async function handleUser(response){ if (response.status == UNAUTHORIZED) { - redirect('login.html?return_to='+encodeURIComponent(window.location.href)); + login(); return; } if (response.ok){