diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0fefd34..baaac27 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## Unreleased
+### Removed
+
+* Removed support for authentication via the now deprecated OpenID 1.0 / OpenID 2.0 protocols.
## [4.1.3] - 2026-04-28
@@ -20,3 +23,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
* Update transitive dependency on org.bouncycastle:bcprov-jdk18on to 1.84 to address dependabot-reported vulnerabilities.
+## [4.0.0] - 2025-12-16
+
+* Move from Java 11 to 21
+* Update appbase dependency to 4.0.0
+* Update derby 10.14 -> 10.17
+* Move from javax.servlet -> jakarta.servlet 6.0
+
+## [3.0.3] - 2025-07-24
+
+* Update shiro to 1.13.0 to avoid most severe CVEs. Move to Shiro2.x would be breaking and there's no migration documentation.
+* Update appbase dependency to pull in update to tomcat 9.0
+
diff --git a/README.md b/README.md
index e65d16b..98af72b 100644
--- a/README.md
+++ b/README.md
@@ -6,19 +6,10 @@ appbase-security
* Provides a `UserStore` for holding registered users and associated permissions, optionally including password credentials. Includes a database-backed store based on embedded [Derby](https://db.apache.org/derby/) and a memory based implementation which is loaded from a configuration file.
* Provides a Realm implementation which has an associated `UserStore` and allows authentication tokens which are externally validated.
* Permissions structure is based on Shiro Wildcard permissions but assumes a simplified pattern of `"{action}:{location}"`. Permissions can be retrieved by location as well as by user.
- * Provides packaged access to an [OpenID](http://openid.net/) implementation so it's easy to create applications where you can register and login using OpenID such as Google.
## Change log:
-**4.0.0**
- * Move from Java 11 to 21
- * Update appbase dependency to 4.0.0
- * Update derby 10.14 -> 10.17
- * Move from javax.servlet -> jakarta.servlet 6.0
-
-**3.0.3**
- * Update shiro to 1.13.0 to avoid most severe CVEs. Move to Shiro2.x would be breaking and there's no migration documentation.
- * Update appbase dependency to pull in update to tomcat 9.0
+See the separate [CHANGELOG.md](CHANGELOG.md).
## Usage
@@ -157,18 +148,10 @@ user id "name" password
For example:
```INI
-user https://profiles.google.com/1147194443288764760228 "Alice"
user dave@epimorphics.com "Dave Reynolds" shouldbechanged
```
-An OpenID profile for anyone with a Google account can be obtained from their profile or
-Google-plus home page and copying the long number from there into the above URL pattern.
-A general Google login generates an OpenID which depends on the requesting web site
-as well as the user. To determine the ID in that case start the bootstrap registry,
-register the target user and note the resulting OpenID, then shutdown the registry
-and modify the initialization file accordingly.
-
-There is a built-in anonymous user with pseudo OpenID of `http://localhost/anon`.
+There is a built-in anonymous user with pseudo id of `http://localhost/anon`.
It is convenient to declare that user in the initialization file as well, so as to bind a
visible name to that id. For example:
@@ -267,111 +250,6 @@ public class SecurityViolationMapper implements
}
```
-## OpenID, login and registration
-
-The `Login` class provides a set of convenience methods to enable user registration
-and login via OpenID, login via password credentials and logout.
-
-To use this you need to provide a set of URL endpoints which invoke the various actions
-and handle OpenID response processing. The easy way to do this is via
-[Jersey](https://jersey.java.net/index.html). For example:
-
-```java
-@Path("/system/security")
-public class LoginCmds {
- protected @Context UriInfo uriInfo;
- protected @Context ServletContext context;
-
- // request OpenID login for a registered user
- @Path("/login")
- @POST
- public Response login(
- @FormParam("provider") String provider,
- @FormParam("return") String returnURL,
- @Context HttpServletRequest request,
- @Context HttpServletResponse response) {
- OpenidRequest oid = new OpenidRequest(uriInfo.getBaseUri().toString() +
- "system/security/response");
- oid.setProvider(provider);
- oid.setReturnURL(returnURL);
- try {
- processOpenID(request, response, oid);
- } catch (Exception e) {
- throw new WebApiException(Status.BAD_REQUEST,
- "Login/registration action failed: " + e);
- }
- return Response.ok().build();
- }
-
- // Register a new user via OpenID
- @Path("/register")
- @POST
- public Response register(
- @FormParam("provider") String provider,
- @FormParam("return") String returnURL,
- @Context HttpServletRequest request,
- @Context HttpServletResponse response) {
- OpenidRequest oid = new OpenidRequest(uriInfo.getBaseUri().toString() +
- "system/security/response");
- oid.setProvider(provider);
- oid.setReturnURL(returnURL);
- oid.setRegister(true);
- try {
- processOpenID(request, response, oid);
- } catch (Exception e) {
- throw new WebApiException(Status.BAD_REQUEST,
- "Login/registration action failed: " + e);
- }
- return Response.ok().build();
- }
-
- // Logout the current loged in user
- @Path("/logout")
- @POST
- public void doLogout(@Context HttpServletRequest request,
- @Context HttpServletResponse response) throws IOException {
- logout(request);
- response.sendRedirect(request.getServletContext().getContextPath());
- }
-
- // Internal endpoint use in the OpenID handshake
- @Path("/response")
- @GET
- public Response openIDResponse(@Context HttpServletRequest request,
- @Context HttpServletResponse response) {
- try {
- UserStore userstore = AppConfig.getApp()
- .getComponentAs("userstore", UserStore.class);
- return redirectTo( verifyResponse(request, response, userstore) );
- } catch (Exception e) {
- return renderError( e.getMessage() );
- }
- }
-
- private Response redirectTo(String path) {
- URI uri;
- try {
- uri = new URI(path);
- return Response.seeOther(uri).build();
- } catch (URISyntaxException e) {
- throw new EpiException(e);
- }
- }
-
- // Some means to report login errors, this assumes AppBase velocity rendering using a generic error.vm template
- private Response renderError(String message) {
- VelocityRender velocity = AppConfig.getApp()
- .getComponentAs("velocity", VelocityRender.class);
- StreamingOutput out = velocity.render("error.vm",
- uriInfo.getPath(),
- context,
- uriInfo.getQueryParameters(),
- "message", message);
- return Response.status(Status.BAD_REQUEST).entity(out).build();
- }
-}
-```
-
## Other
The `UserStore` implementation also provides various methods for accessing available
diff --git a/pom.xml b/pom.xml
index afd667e..5c36c7d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -107,40 +107,6 @@
3.20.0
-
- org.openid4java
- openid4java
- 1.0.0
-
-
- org.apache.httpcomponents
- httpclient
-
-
- org.apache.httpcomponents
- httpcore
-
-
- xerces
- xercesImpl
-
-
- net.sourceforge.nekohtml
- nekohtml
-
-
-
-
- xerces
- xercesImpl
- 2.12.2
-
-
- net.sourceforge.nekohtml
- nekohtml
- 1.9.22
-
-
org.apache.shiro
shiro-core
diff --git a/src/main/java/com/epimorphics/appbase/security/Login.java b/src/main/java/com/epimorphics/appbase/security/Login.java
index 5cfbe48..b0c9575 100644
--- a/src/main/java/com/epimorphics/appbase/security/Login.java
+++ b/src/main/java/com/epimorphics/appbase/security/Login.java
@@ -32,7 +32,7 @@
import com.epimorphics.util.EpiException;
/**
- * Utility functions for registration and login via OpenID.
+ * Utility functions for registration and login.
* Binding these to resource URIs via jersey in the web application.
*
* @author Dave Reynolds
diff --git a/src/main/java/com/epimorphics/appbase/security/ProcessOpenID.java b/src/main/java/com/epimorphics/appbase/security/ProcessOpenID.java
deleted file mode 100644
index d50319b..0000000
--- a/src/main/java/com/epimorphics/appbase/security/ProcessOpenID.java
+++ /dev/null
@@ -1,274 +0,0 @@
-/******************************************************************
- * File: ProcessOpenID.java
- * Created by: Dave Reynolds
- * Created on: 15 Jul 2014
- *
- * (c) Copyright 2014, Epimorphics Limited
- *
- *****************************************************************/
-
-package com.epimorphics.appbase.security;
-
-import java.util.List;
-import java.util.Map;
-
-import jakarta.servlet.http.Cookie;
-import jakarta.servlet.http.HttpServletRequest;
-import jakarta.servlet.http.HttpServletResponse;
-import jakarta.servlet.http.HttpSession;
-
-import org.apache.shiro.SecurityUtils;
-import org.apache.shiro.subject.Subject;
-import org.openid4java.consumer.ConsumerManager;
-import org.openid4java.consumer.VerificationResult;
-import org.openid4java.discovery.DiscoveryInformation;
-import org.openid4java.discovery.Identifier;
-import org.openid4java.message.AuthRequest;
-import org.openid4java.message.AuthSuccess;
-import org.openid4java.message.ParameterList;
-import org.openid4java.message.ax.AxMessage;
-import org.openid4java.message.ax.FetchRequest;
-import org.openid4java.message.ax.FetchResponse;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.epimorphics.util.EpiException;
-
-public class ProcessOpenID {
- static final Logger log = LoggerFactory.getLogger( ProcessOpenID.class );
-
- public static final String DEFAULT_PROVIDER = "https://www.google.com/accounts/o8/id";
- public static final String PROVIDER_COOKIE = "appbase-login-provider";
-
- // Session attribute names
- public static final String SA_OPENID_DISC = "openid_disc";
- public static final String SA_OPENID_PROVIDER = "openid_provider";
- public static final String SA_REGISTRATION = "isRegistration";
- public static final String SA_RETURN_URL = "returnURL";
-
- // Attribute parameter names
- public static final String AP_EMAIL = "email";
- public static final String AP_FIRST_NAME = "firstName";
- public static final String AP_LAST_NAME = "lastName";
- public static final String AP_FULL_NAME = "fullname";
-
- // Velocity binding names
- public static final String VN_REGISTRATION_STATUS = "registrationStatus";
- public static final String RS_NEW = "new";
- public static final String RS_ALREADY_REGISTERED = "already";
- public static final String RS_LOGIN = "login";
-
- private static ConsumerManager manager = null;
- static {
- try {
- manager = new ConsumerManager();
- } catch (Exception e) {
- log.error("Failed to initialize openid subsystem", e);
- }
- }
-
- /**
- * Perform a login or registration via OpenID.
- * @throws EpiException if the request is malformed in some way.
- */
- @SuppressWarnings("rawtypes")
- static public void processOpenID(HttpServletRequest request, HttpServletResponse response, OpenidRequest oid) {
- HttpSession session = request.getSession();
- session.setAttribute(SA_REGISTRATION, oid.isRegister());
- session.setAttribute(SA_OPENID_PROVIDER, oid.getProvider());
- session.setAttribute(SA_RETURN_URL, oid.getReturnURL());
-
- log.info("Authentication request for " + oid.getProvider() + (oid.isRegister() ? " (registration)" : ""));
-
- try
- {
- // perform discovery on the user-supplied identifier
- List discoveries = manager.discover(oid.getProvider());
-
- // attempt to associate with the OpenID provider
- // and retrieve one service endpoint for authentication
- DiscoveryInformation discovered = manager.associate(discoveries);
-
- // store the discovery information in the user's session
- request.getSession().setAttribute(SA_OPENID_DISC, discovered);
-
- // obtain a AuthRequest message to be sent to the OpenID provider
- AuthRequest authReq = manager.authenticate(discovered, oid.getResponseURL());
-
- if (oid.isRegister()) {
- // Attribute Exchange example: fetching the 'email' attribute
- FetchRequest fetch = FetchRequest.createFetchRequest();
- if (oid.getProvider().contains("google.com")) {
-// fetch.addAttribute(AP_EMAIL, "http://axschema.org/contact/email", false);
- fetch.addAttribute(AP_FIRST_NAME, "http://axschema.org/namePerson/first", true);
- fetch.addAttribute(AP_LAST_NAME, "http://axschema.org/namePerson/last", true);
- } else if (oid.getProvider().contains("yahoo.com")) {
-// fetch.addAttribute(AP_EMAIL, "http://axschema.org/contact/email", false);
- fetch.addAttribute(AP_FULL_NAME, "http://axschema.org/namePerson", true);
- } else { //works for myOpenID
-// fetch.addAttribute(AP_EMAIL, "http://schema.openid.net/contact/email", false);
- fetch.addAttribute(AP_FULL_NAME, "http://schema.openid.net/namePerson", true);
- }
-
- // attach the extension to the authentication request
- authReq.addExtension(fetch);
- }
-
- // For version2 endpoints can do a form-redirect but this is easier,
- // Relies on payload being less ~ 2k, currently ~ 800 bytes
- response.sendRedirect(authReq.getDestinationUrl(true));
- }
- catch (Exception e)
- {
- throw new EpiException("Login/registration action failed: " + e);
- }
- }
-
- /**
- * Process the verification response from the OpenID provider. This should be called
- * from a URL which is given as part of the original OpenIDRequest. If the verification
- * was successful it returns the URL to which the user should be redirected (specified
- * in the original call), otherwise an EpiExpception is thrown.
- */
- @SuppressWarnings({ "unchecked" })
- static public String verifyResponse(HttpServletRequest request, HttpServletResponse httpresponse, UserStore userstore) {
- try {
- HttpSession session = request.getSession();
-
- // extract the parameters from the authentication response
- // (which comes in as a HTTP request from the OpenID provider)
- ParameterList response =
- new ParameterList(request.getParameterMap());
-
- // retrieve the previously stored discovery information
- DiscoveryInformation discovered = (DiscoveryInformation)
- session.getAttribute("openid-disc");
-
- // extract the receiving URL from the HTTP request
- StringBuffer receivingURL = request.getRequestURL();
- String queryString = request.getQueryString();
- if (queryString != null && queryString.length() > 0)
- receivingURL.append("?").append(request.getQueryString());
-
- // verify the response; ConsumerManager needs to be the same
- // (static) instance used to place the authentication request
- VerificationResult verification = manager.verify(
- receivingURL.toString(),
- response, discovered);
-
- // examine the verification result and extract the verified identifier
- Identifier verified = verification.getVerifiedId();
- if (verified != null) {
- AuthSuccess authSuccess = (AuthSuccess) verification.getAuthResponse();
- String name = null;
- if (authSuccess.hasExtension(AxMessage.OPENID_NS_AX)) {
- FetchResponse fetchResp = (FetchResponse) authSuccess
- .getExtension(AxMessage.OPENID_NS_AX);
- Map> attributes = fetchResp.getAttributes();
- if (attributes.containsKey(AP_FULL_NAME)) {
- name = attributes.get(AP_FULL_NAME).get(0);
- } else {
- name = attributes.get(AP_FIRST_NAME).get(0) + " " + attributes.get(AP_LAST_NAME).get(0);
- }
- }
- log.info(String.format("Verified identity %s = %s", verified.getIdentifier(), name));
- boolean isRegistration = ((Boolean)session.getAttribute(SA_REGISTRATION)).booleanValue();
- String registrationStatus = RS_LOGIN;
- if (isRegistration) {
- UserInfo userinfo = new UserInfo(verified.getIdentifier(), name);
- if (userstore.register( userinfo )) {
- registrationStatus = RS_NEW;
- } else {
- registrationStatus = RS_ALREADY_REGISTERED;
- }
- }
-
- AppRealmToken token = new AppRealmToken(verified.getIdentifier(), true);
- Subject subject = SecurityUtils.getSubject();
- try {
- subject.login(token);
- session.setAttribute(VN_REGISTRATION_STATUS, registrationStatus);
- String provider = (String)session.getAttribute(SA_OPENID_PROVIDER);
- if (provider != null && !provider.isEmpty()) {
- Cookie cookie = new Cookie(PROVIDER_COOKIE, provider);
- cookie.setMaxAge(60 * 60 * 24 * 30);
- cookie.setHttpOnly(true);
- cookie.setPath("/");
- httpresponse.addCookie(cookie);
- }
- return session.getAttribute(SA_RETURN_URL).toString();
- } catch (Exception e) {
- log.error("Authentication failure", e);
- throw new EpiException("Could not find a registration.");
- }
- }
- } catch (Exception e) {
- throw new EpiException(e);
- }
- throw new EpiException("OpenID login failed");
- }
-
-
- /**
- * Packaged set of parameters for an OpenID login or registration request.
- *
- * @author Dave Reynolds
- */
- static public class OpenidRequest {
- String provider = DEFAULT_PROVIDER;
- String responseURL;
- String returnURL = "/";
- boolean isRegister = false;
-
- /**
- * Create a login or registration request
- * @param responseURL The URL to use for the OpenID response, this endpoint should invoke a verifyRequest call
- */
- public OpenidRequest(String responseURL) {
- this.responseURL = responseURL;
- }
-
- /**
- * Set the OpenID provider to use. The default is generic Google login (which is
- * distinct from a person-specific Google profile provider)
- */
- public void setProvider(String provider) {
- if (provider == null) {
- this.provider = DEFAULT_PROVIDER;
- } else {
- this.provider = provider;
- }
- }
-
- /**
- * Set the URL to which the user will be redirected after a successful login
- */
- public void setReturnURL(String returnURL) {
- this.returnURL = returnURL;
- }
-
- /**
- * Set to true if this is a registration rather than a login (default is login)
- */
- public void setRegister(boolean isRegister) {
- this.isRegister = isRegister;
- }
-
- public String getProvider() {
- return provider;
- }
-
- public String getResponseURL() {
- return responseURL;
- }
-
- public String getReturnURL() {
- return returnURL;
- }
-
- public boolean isRegister() {
- return isRegister;
- }
-
- }
-}