Skip to content

Commit

Permalink
A proposal for injectable built-in auth mechanisms
Browse files Browse the repository at this point in the history
Signed-off-by:Ondro Mihalyi <[email protected]>
  • Loading branch information
OndroMih committed Apr 10, 2023
1 parent 3ad6a6a commit d376fda
Show file tree
Hide file tree
Showing 10 changed files with 272 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,4 @@ target/
/**/.classpath
/**/.project
/**/.gitignore
/injectable-ham/src/main/webapp/WEB-INF/glassfish-web.xml
39 changes: 39 additions & 0 deletions injectable-ham/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Proposal to inject built-in HttpAuthenticationMechanism beans

This is to make it possible to choose programmatically which of the built-in authentication mechanisms should be used based on configuration or user input. It would be possible to inject one or more mechanisms configured using the existing definition annotations (e.g. `@BasicAuthenticationMechanismDefinition`) and delegate authentication to them from a custom HttpAuthenticationMechanism bean.

## Example

```
@ApplicationScoped
public class CustomAuth implements HttpAuthenticationMechanism {
@Inject
@FormAuthenticationMechanismDefinition(loginToContinue = @LoginToContinue(loginPage = "/login.xhtml"))
HttpAuthenticationMechanism formAuthentication;
@Override
public AuthenticationStatus validateRequest(HttpServletRequest request, HttpServletResponse response, HttpMessageContext context) throws AuthenticationException {
return formAuthentication.validateRequest(request, response, context);
}
}
```

## Use cases

### Select which OpenID provider to use

When an application wants to support multiple OpenID providers (Google, Auth0, a custom provider). The user should be able to select which provider they want to use to authenticate. E.g. if they already have a Google account, they would select Google as their provider. Their selection can be implemented for example as a cookie or a query parameter in the login action URL. A custom auth mechanism would detect this selection from the request object and delegate to one of the injected built-in OpenID mechanisms.

### Select between OpenID authentication and form-based (user/password) authentication

When an application wants to support OpenID on top of it's own form/based authentication mechanism. Users already have accounts in the system, can log in using user/password. Additionally, they have an option to connect their OpenID account with their existing account and log in using the OpenID acocunt besides using user/password.

### Multitenant applications

When the application supports multiple tenants (customers, user groups, etc.), it's desirable that each group of users using a different authentication mechanism. If at least one of the mechanisms uses a built-in mechanism (e.g. form-based or OpenID-Connect), these mechanisms can be simply injected into a custom mechanism and reused for the specific tenant.

A simple multitenancy with a fixed number of tenants can be implemented simply by injecting an authentication mechanism per tenant.

Dynamic multitenancy (with configurable number of tenants) is not possible using this approach and is out of scope of this proposal. Dynamic multitenancy would require some API (e.g. a CDI bean) which would provide configuration based on the current tenant. It would also be required that the built-in mechanisms, which are application-scoped, support reconfiguration per session/request.
82 changes: 82 additions & 0 deletions injectable-ham/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>javaee-security-examples</artifactId>
<groupId>net.java.jsr375</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<groupId>net.java.jsr375</groupId>
<artifactId>injectable-ham</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<name>injectable-ham-1.0-SNAPSHOT</name>

<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<endorsed.dir>${project.build.directory}/endorsed</endorsed.dir>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<failOnMissingWebXml>false</failOnMissingWebXml>
<jakartaee>10.0.0</jakartaee>
</properties>

<dependencies>
<dependency>
<groupId>jakarta.platform</groupId>
<artifactId>jakarta.jakartaee-api</artifactId>
<version>${jakartaee}</version>
<scope>provided</scope>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>11</source>
<target>11</target>
<compilerArguments>
<endorseddirs>${endorsed.dir}</endorseddirs>
</compilerArguments>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>3.3.2</version>
<configuration>
<failOnMissingWebXml>false</failOnMissingWebXml>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.6</version>
<executions>
<execution>
<phase>validate</phase>
<goals>
<goal>copy</goal>
</goals>
<configuration>
<outputDirectory>${endorsed.dir}</outputDirectory>
<silent>true</silent>
<artifactItems>
<artifactItem>
<groupId>jakarta.platform</groupId>
<artifactId>jakarta.jakartaee-api</artifactId>
<version>${jakartaee}</version>
<type>jar</type>
</artifactItem>
</artifactItems>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
52 changes: 52 additions & 0 deletions injectable-ham/src/main/java/injectableham/CustomAuth.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package injectableham;

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.inject.spi.CDI;
import jakarta.inject.Inject;
import jakarta.security.enterprise.AuthenticationException;
import jakarta.security.enterprise.AuthenticationStatus;
import jakarta.security.enterprise.authentication.mechanism.http.BasicAuthenticationMechanismDefinition;
import jakarta.security.enterprise.authentication.mechanism.http.FormAuthenticationMechanismDefinition;
import jakarta.security.enterprise.authentication.mechanism.http.HttpAuthenticationMechanism;
import jakarta.security.enterprise.authentication.mechanism.http.HttpMessageContext;
import jakarta.security.enterprise.authentication.mechanism.http.LoginToContinue;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.util.Set;

@ApplicationScoped
public class CustomAuth implements HttpAuthenticationMechanism {

@Inject
@FormAuthenticationMechanismDefinition(loginToContinue = @LoginToContinue(loginPage = "/login.xhtml"))
HttpAuthenticationMechanism formAuthentication;

@Inject
@BasicAuthenticationMechanismDefinition
HttpAuthenticationMechanism basicAuthentication;

private HttpAuthenticationMechanism getMechanism(HttpServletRequest request) {
if ("authType".equals(request.getParameter("form")) ) {
return formAuthentication;
} else {
return basicAuthentication;
}

}

@Override
public AuthenticationStatus validateRequest(HttpServletRequest request, HttpServletResponse response, HttpMessageContext httpMessageContext) throws AuthenticationException {
return getMechanism(request).validateRequest(request, response, httpMessageContext);
}

@Override
public AuthenticationStatus secureResponse(HttpServletRequest request, HttpServletResponse response, HttpMessageContext httpMessageContext) throws AuthenticationException {
return getMechanism(request).secureResponse(request, response, httpMessageContext);
}

@Override
public void cleanSubject(HttpServletRequest request, HttpServletResponse response, HttpMessageContext httpMessageContext) {
getMechanism(request).cleanSubject(request, response, httpMessageContext);
}

}
6 changes: 6 additions & 0 deletions injectable-ham/src/main/webapp/WEB-INF/beans.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="https://jakarta.ee/xml/ns/jakartaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/beans_4_0.xsd"
bean-discovery-mode="annotated">
</beans>
39 changes: 39 additions & 0 deletions injectable-ham/src/main/webapp/WEB-INF/web.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="6.0" xmlns="https://jakarta.ee/xml/ns/jakartaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_6_0.xsd">
<context-param>
<param-name>jakarta.faces.PROJECT_STAGE</param-name>
<param-value>Development</param-value>
</context-param>
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>jakarta.faces.webapp.FacesServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>/faces/*</url-pattern>
</servlet-mapping>
<session-config>
<session-timeout>
30
</session-timeout>
</session-config>
<security-role>
<description>admin</description>
<role-name>admin</role-name>
</security-role>
<welcome-file-list>
<welcome-file>faces/index.xhtml</welcome-file>
</welcome-file-list>
<security-constraint>
<display-name>admin</display-name>
<web-resource-collection>
<web-resource-name>secured</web-resource-name>
<url-pattern>/secured/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<description>admin</description>
<role-name>admin</role-name>
</auth-constraint>
</security-constraint>
</web-app>
17 changes: 17 additions & 0 deletions injectable-ham/src/main/webapp/index.xhtml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
<title>Start Page</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
</head>
<body>
<h1>Hello World!</h1>
<p>
<a href="secured/admin.xhtml">Secured admin - unauthorized</a>
</p>
<p>
<a href="secured/admin.xhtml?user=admin">Secured admin - authorized</a>
</p>
</body>
</html>
24 changes: 24 additions & 0 deletions injectable-ham/src/main/webapp/login.xhtml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html">
<h:head>
<title>Login form</title>
</h:head>
<h:body>


<form style="margin: 1rem" method="POST" action="j_security_check">
<p>User:
<input type="text" name="j_username"/>
</p>
<p>Password:
<input type="password" name="j_password" autocomplete="off"/>
</p>
<p><button type="submit">Log in</button>
</p>
</form>


</h:body>
</html>
11 changes: 11 additions & 0 deletions injectable-ham/src/main/webapp/secured/admin.xhtml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html">
<h:head>
<title>Facelet Title</title>
</h:head>
<h:body>
Hello from Facelets
</h:body>
</html>
1 change: 1 addition & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
<module>roles-allowed-and-runas</module>
<module>simple-jaspic-example</module>
<module>rest-basic-example</module>
<module>injectable-ham</module>
</modules>

<build>
Expand Down

0 comments on commit d376fda

Please sign in to comment.