last oauth
This commit is contained in:
parent
579aefd310
commit
15a947f4ae
19 changed files with 420 additions and 38 deletions
|
@ -28,6 +28,7 @@ public class AbstractOAuthClient {
|
||||||
protected String codeUrl;
|
protected String codeUrl;
|
||||||
protected String stateCookieName = "OAuth_Token_Request_State";
|
protected String stateCookieName = "OAuth_Token_Request_State";
|
||||||
protected Client client;
|
protected Client client;
|
||||||
|
protected boolean isSecure;
|
||||||
protected final AtomicLong counter = new AtomicLong();
|
protected final AtomicLong counter = new AtomicLong();
|
||||||
|
|
||||||
protected String getStateCode() {
|
protected String getStateCode() {
|
||||||
|
@ -109,6 +110,8 @@ public class AbstractOAuthClient {
|
||||||
Form codeForm = new Form()
|
Form codeForm = new Form()
|
||||||
.param("grant_type", "authorization_code")
|
.param("grant_type", "authorization_code")
|
||||||
.param("code", code)
|
.param("code", code)
|
||||||
|
.param("client_id", clientId)
|
||||||
|
.param("Password", password)
|
||||||
.param("redirect_uri", redirectUri);
|
.param("redirect_uri", redirectUri);
|
||||||
Response res = client.target(codeUrl).request().header(HttpHeaders.AUTHORIZATION, authHeader).post(Entity.form(codeForm));
|
Response res = client.target(codeUrl).request().header(HttpHeaders.AUTHORIZATION, authHeader).post(Entity.form(codeForm));
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -55,7 +55,7 @@ public class ServletOAuthClient extends AbstractOAuthClient {
|
||||||
if (cookiePath.equals("")) cookiePath = "/";
|
if (cookiePath.equals("")) cookiePath = "/";
|
||||||
|
|
||||||
Cookie cookie = new Cookie(stateCookieName, state);
|
Cookie cookie = new Cookie(stateCookieName, state);
|
||||||
cookie.setSecure(true);
|
cookie.setSecure(isSecure);
|
||||||
cookie.setPath(cookiePath);
|
cookie.setPath(cookiePath);
|
||||||
response.addCookie(cookie);
|
response.addCookie(cookie);
|
||||||
response.sendRedirect(url.toString());
|
response.sendRedirect(url.toString());
|
||||||
|
|
|
@ -25,16 +25,34 @@
|
||||||
{ "type" : "Password",
|
{ "type" : "Password",
|
||||||
"value" : "password" }
|
"value" : "password" }
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"username" : "third-party",
|
||||||
|
"enabled" : true,
|
||||||
|
"credentials" : [
|
||||||
|
{ "type" : "Password",
|
||||||
|
"value" : "password" }
|
||||||
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"roles" : [
|
"roles" : [
|
||||||
{ "name" : "user", "description" : "User privileges" },
|
{ "name" : "user", "description" : "Have User privileges" },
|
||||||
{ "name" : "admin", "description" : "Administrator privileges" }
|
{ "name" : "admin", "description" : "Have Administrator privileges" }
|
||||||
],
|
],
|
||||||
"roleMappings" : [
|
"roleMappings" : [
|
||||||
{
|
{
|
||||||
"username" : "bburke@redhat.com",
|
"username" : "bburke@redhat.com",
|
||||||
"roles" : ["user"]
|
"roles" : ["user"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"username" : "third-party",
|
||||||
|
"roles" : ["KEYCLOAK_IDENTITY_REQUESTER"]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"scopeMappings" : [
|
||||||
|
{
|
||||||
|
"username" : "third-party",
|
||||||
|
"roles" : ["user"]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"resources" : [
|
"resources" : [
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<title>Keycloak</title>
|
<title>Keycloak Realm Login Page</title>
|
||||||
|
|
||||||
<link rel="shortcut icon" type="image/x-icon" href="<%=application.getContextPath()%>/img/favicon.ico">
|
<link rel="shortcut icon" type="image/x-icon" href="<%=application.getContextPath()%>/img/favicon.ico">
|
||||||
|
|
||||||
|
|
82
examples/as7-eap-demo/server/src/main/webapp/oauthGrantForm.jsp
Executable file
82
examples/as7-eap-demo/server/src/main/webapp/oauthGrantForm.jsp
Executable file
|
@ -0,0 +1,82 @@
|
||||||
|
<%@ page import="org.picketlink.idm.model.*,org.keycloak.services.models.*,org.keycloak.services.resources.*,javax.ws.rs.core.*,java.util.*" language="java" contentType="text/html; charset=ISO-8859-1"
|
||||||
|
pageEncoding="ISO-8859-1"%>
|
||||||
|
<%
|
||||||
|
RealmModel realm = (RealmModel)request.getAttribute(RealmModel.class.getName());
|
||||||
|
String username = (String)request.getAttribute("username");
|
||||||
|
%>
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>Keycloak</title>
|
||||||
|
|
||||||
|
<link rel="shortcut icon" type="image/x-icon" href="<%=application.getContextPath()%>/img/favicon.ico">
|
||||||
|
|
||||||
|
<link href="<%=application.getContextPath()%>/lib/bootstrap/css/bootstrap.css" rel="stylesheet">
|
||||||
|
<link href="<%=application.getContextPath()%>/lib/font-awesome/css/font-awesome.css" rel="stylesheet">
|
||||||
|
<link href="<%=application.getContextPath()%>/css/reset.css" rel="stylesheet">
|
||||||
|
<link href="<%=application.getContextPath()%>/css/base.css" rel="stylesheet">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<%
|
||||||
|
User client = (User)request.getAttribute("client");
|
||||||
|
List<Role> realmRolesRequested = (List<Role>)request.getAttribute("realmRolesRequested");
|
||||||
|
MultivaluedMap<String, Role> resourceRolesRequested = (MultivaluedMap<String, Role>)request.getAttribute("resourceRolesRequested");
|
||||||
|
%>
|
||||||
|
|
||||||
|
<h1>Grant request for: <%=client.getLoginName()%></h1>
|
||||||
|
<div class="modal-body">
|
||||||
|
|
||||||
|
|
||||||
|
<p>This app would like to:</p>
|
||||||
|
<hr/>
|
||||||
|
<%
|
||||||
|
if (realmRolesRequested.size() > 0) {
|
||||||
|
%> <ul> <%
|
||||||
|
for (Role role : realmRolesRequested) {
|
||||||
|
String desc = "Have " + role.getName() + " privileges.";
|
||||||
|
Attribute roleDesc = role.getAttribute("description");
|
||||||
|
if (roleDesc != null) {
|
||||||
|
desc = (String)roleDesc.getValue();
|
||||||
|
}
|
||||||
|
%>
|
||||||
|
<li><%=desc%></li>
|
||||||
|
<%
|
||||||
|
}
|
||||||
|
%> </ul> <%
|
||||||
|
}
|
||||||
|
for (String resource : resourceRolesRequested.keySet()) {
|
||||||
|
List<Role> roles = resourceRolesRequested.get(resource);
|
||||||
|
out.println("<i>For application " + resource + ":</i> ");
|
||||||
|
out.println("<ul>");
|
||||||
|
for (Role role : roles) {
|
||||||
|
String desc = "Have " + role.getName() + " privileges.";
|
||||||
|
Attribute roleDesc = role.getAttribute("description");
|
||||||
|
if (roleDesc != null) {
|
||||||
|
desc = (String)roleDesc.getValue();
|
||||||
|
}
|
||||||
|
out.println("<li>" + desc + "</li>");
|
||||||
|
}
|
||||||
|
out.println("</ul>");
|
||||||
|
}
|
||||||
|
%>
|
||||||
|
<hr/>
|
||||||
|
|
||||||
|
|
||||||
|
<form class="form-horizontal" name="oauthGrant" action="<%=request.getAttribute("action")%>" method="POST">
|
||||||
|
<input type="hidden" name="code" value="<%=request.getAttribute("code")%>">
|
||||||
|
<div class="control-group">
|
||||||
|
<div class="controls">
|
||||||
|
<input type="submit" name="accept" class="btn btn-primary" value="Accept">
|
||||||
|
<input type="submit" name="cancel" class="btn btn-primary" value="Cancel">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<footer>
|
||||||
|
<p>Powered By Keycloak</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
61
examples/as7-eap-demo/third-party/pom.xml
vendored
Executable file
61
examples/as7-eap-demo/third-party/pom.xml
vendored
Executable file
|
@ -0,0 +1,61 @@
|
||||||
|
<?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/maven-v4_0_0.xsd">
|
||||||
|
<parent>
|
||||||
|
<artifactId>keycloak-parent</artifactId>
|
||||||
|
<groupId>org.keycloak</groupId>
|
||||||
|
<version>1.0-alpha-1</version>
|
||||||
|
<relativePath>../../../pom.xml</relativePath>
|
||||||
|
</parent>
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<groupId>org.keycloak.example.as7.demo</groupId>
|
||||||
|
<artifactId>oauth-client-example</artifactId>
|
||||||
|
<packaging>war</packaging>
|
||||||
|
<name>Simple OAuth Client</name>
|
||||||
|
<description/>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.jboss.spec.javax.servlet</groupId>
|
||||||
|
<artifactId>jboss-servlet-api_3.0_spec</artifactId>
|
||||||
|
<version>1.0.1.Final</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.jboss.resteasy</groupId>
|
||||||
|
<artifactId>resteasy-client</artifactId>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.keycloak</groupId>
|
||||||
|
<artifactId>keycloak-core</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<finalName>oauth-client</finalName>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.jboss.as.plugins</groupId>
|
||||||
|
<artifactId>jboss-as-maven-plugin</artifactId>
|
||||||
|
<version>7.4.Final</version>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-deploy-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<skip>true</skip>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<source>1.6</source>
|
||||||
|
<target>1.6</target>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
</project>
|
69
examples/as7-eap-demo/third-party/src/main/java/org/jboss/resteasy/example/oauth/Bootstrap.java
vendored
Executable file
69
examples/as7-eap-demo/third-party/src/main/java/org/jboss/resteasy/example/oauth/Bootstrap.java
vendored
Executable file
|
@ -0,0 +1,69 @@
|
||||||
|
package org.jboss.resteasy.example.oauth;
|
||||||
|
|
||||||
|
import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder;
|
||||||
|
import org.keycloak.servlet.ServletOAuthClient;
|
||||||
|
|
||||||
|
import javax.servlet.ServletContextEvent;
|
||||||
|
import javax.servlet.ServletContextListener;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.security.KeyStore;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stupid init code to load up the truststore so we can make appropriate SSL connections
|
||||||
|
* You really should use a better way of initializing this stuff.
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
* @version $Revision: 1 $
|
||||||
|
*/
|
||||||
|
public class Bootstrap implements ServletContextListener {
|
||||||
|
|
||||||
|
private ServletOAuthClient client;
|
||||||
|
|
||||||
|
private static KeyStore loadKeyStore(String filename, String password) throws Exception {
|
||||||
|
KeyStore trustStore = KeyStore.getInstance(KeyStore
|
||||||
|
.getDefaultType());
|
||||||
|
File truststoreFile = new File(filename);
|
||||||
|
FileInputStream trustStream = new FileInputStream(truststoreFile);
|
||||||
|
trustStore.load(trustStream, password.toCharArray());
|
||||||
|
trustStream.close();
|
||||||
|
return trustStore;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void contextInitialized(ServletContextEvent sce) {
|
||||||
|
client = new ServletOAuthClient();
|
||||||
|
/*
|
||||||
|
// hardcoded, WARNING, you should really have a better way of doing this
|
||||||
|
// configuration. Either use something like Spring or CDI, or even pull
|
||||||
|
// config vales from context-params
|
||||||
|
String truststorePath = "${jboss.server.config.dir}/client-truststore.ts";
|
||||||
|
String truststorePassword = "password";
|
||||||
|
truststorePath = EnvUtil.replace(truststorePath);
|
||||||
|
KeyStore truststore = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
truststore = loadKeyStore(truststorePath, truststorePassword);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
client.setTruststore(truststore);
|
||||||
|
*/
|
||||||
|
client.setClientId("third-party");
|
||||||
|
client.setPassword("password");
|
||||||
|
client.setAuthUrl("http://localhost:8080/auth-server/rest/realms/demo/tokens/login");
|
||||||
|
client.setCodeUrl("http://localhost:8080/auth-server/rest/realms/demo/tokens/access/codes");
|
||||||
|
client.setClient(new ResteasyClientBuilder().build());
|
||||||
|
client.start();
|
||||||
|
sce.getServletContext().setAttribute(ServletOAuthClient.class.getName(), client);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void contextDestroyed(ServletContextEvent sce) {
|
||||||
|
client.stop();
|
||||||
|
}
|
||||||
|
}
|
69
examples/as7-eap-demo/third-party/src/main/java/org/jboss/resteasy/example/oauth/ProductDatabaseClient.java
vendored
Executable file
69
examples/as7-eap-demo/third-party/src/main/java/org/jboss/resteasy/example/oauth/ProductDatabaseClient.java
vendored
Executable file
|
@ -0,0 +1,69 @@
|
||||||
|
package org.jboss.resteasy.example.oauth;
|
||||||
|
|
||||||
|
import org.jboss.resteasy.client.jaxrs.ResteasyClient;
|
||||||
|
import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder;
|
||||||
|
import org.keycloak.servlet.ServletOAuthClient;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import javax.ws.rs.core.GenericType;
|
||||||
|
import javax.ws.rs.core.HttpHeaders;
|
||||||
|
import javax.ws.rs.core.Response;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
* @version $Revision: 1 $
|
||||||
|
*/
|
||||||
|
public class ProductDatabaseClient {
|
||||||
|
public static void redirect(HttpServletRequest request, HttpServletResponse response) {
|
||||||
|
// This is really the worst code ever. The ServletOAuthClient is obtained by getting a context attribute
|
||||||
|
// that is set in the Bootstrap context listenr in this project.
|
||||||
|
// You really should come up with a better way to initialize
|
||||||
|
// and obtain the ServletOAuthClient. I actually suggest downloading the ServletOAuthClient code
|
||||||
|
// and take a look how it works.
|
||||||
|
ServletOAuthClient oAuthClient = (ServletOAuthClient) request.getServletContext().getAttribute(ServletOAuthClient.class.getName());
|
||||||
|
try {
|
||||||
|
oAuthClient.redirectRelative("pull_data.jsp", request, response);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<String> getProducts(HttpServletRequest request) {
|
||||||
|
// This is really the worst code ever. The ServletOAuthClient is obtained by getting a context attribute
|
||||||
|
// that is set in the Bootstrap context listenr in this project.
|
||||||
|
// You really should come up with a better way to initialize
|
||||||
|
// and obtain the ServletOAuthClient. I actually suggest downloading the ServletOAuthClient code
|
||||||
|
// and take a look how it works.
|
||||||
|
ServletOAuthClient oAuthClient = (ServletOAuthClient) request.getServletContext().getAttribute(ServletOAuthClient.class.getName());
|
||||||
|
String token = oAuthClient.getBearerToken(request);
|
||||||
|
ResteasyClient client = new ResteasyClientBuilder()
|
||||||
|
.trustStore(oAuthClient.getTruststore())
|
||||||
|
.hostnameVerification(ResteasyClientBuilder.HostnameVerificationPolicy.ANY).build();
|
||||||
|
try {
|
||||||
|
// invoke without the Authorization header
|
||||||
|
Response response = client.target("http://localhost:8080/database/products").request().get();
|
||||||
|
response.close();
|
||||||
|
if (response.getStatus() != 401) {
|
||||||
|
response.close();
|
||||||
|
client.close();
|
||||||
|
throw new RuntimeException("Expecting an auth status code: " + response.getStatus());
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Response response = client.target("http://localhost:8080/database/products").request()
|
||||||
|
.header(HttpHeaders.AUTHORIZATION, "Bearer " + token).get();
|
||||||
|
if (response.getStatus() != 200) {
|
||||||
|
response.close();
|
||||||
|
throw new RuntimeException("Failed to access!: " + response.getStatus());
|
||||||
|
}
|
||||||
|
return response.readEntity(new GenericType<List<String>>() {
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
client.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
9
examples/as7-eap-demo/third-party/src/main/webapp/WEB-INF/jboss-deployment-structure.xml
vendored
Executable file
9
examples/as7-eap-demo/third-party/src/main/webapp/WEB-INF/jboss-deployment-structure.xml
vendored
Executable file
|
@ -0,0 +1,9 @@
|
||||||
|
<jboss-deployment-structure>
|
||||||
|
<deployment>
|
||||||
|
<!-- This allows you to define additional dependencies, it is the same as using the Dependencies: manifest attribute -->
|
||||||
|
<dependencies>
|
||||||
|
<module name="org.jboss.resteasy.resteasy-jaxrs" services="import"/>
|
||||||
|
<module name="org.jboss.resteasy.resteasy-jackson-provider" services="import"/>
|
||||||
|
</dependencies>
|
||||||
|
</deployment>
|
||||||
|
</jboss-deployment-structure>
|
20
examples/as7-eap-demo/third-party/src/main/webapp/WEB-INF/web.xml
vendored
Executable file
20
examples/as7-eap-demo/third-party/src/main/webapp/WEB-INF/web.xml
vendored
Executable file
|
@ -0,0 +1,20 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
|
||||||
|
version="3.0">
|
||||||
|
<listener>
|
||||||
|
<listener-class>org.jboss.resteasy.example.oauth.Bootstrap</listener-class>
|
||||||
|
</listener>
|
||||||
|
<!--
|
||||||
|
<security-constraint>
|
||||||
|
<web-resource-collection>
|
||||||
|
<url-pattern>/*</url-pattern>
|
||||||
|
</web-resource-collection>
|
||||||
|
<user-data-constraint>
|
||||||
|
<transport-guarantee>CONFIDENTIAL</transport-guarantee>
|
||||||
|
</user-data-constraint>
|
||||||
|
</security-constraint>
|
||||||
|
-->
|
||||||
|
|
||||||
|
</web-app>
|
6
examples/as7-eap-demo/third-party/src/main/webapp/index.html
vendored
Normal file
6
examples/as7-eap-demo/third-party/src/main/webapp/index.html
vendored
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
<h1>Third Party App That Pulls Data Using OAuth</h1>
|
||||||
|
<a href="redirect.jsp">Pull Data</a>
|
||||||
|
</body>
|
||||||
|
</html>
|
21
examples/as7-eap-demo/third-party/src/main/webapp/pull_data.jsp
vendored
Normal file
21
examples/as7-eap-demo/third-party/src/main/webapp/pull_data.jsp
vendored
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
|
||||||
|
pageEncoding="ISO-8859-1"%>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Pull Page</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h2>Pulled Product Listing</h2>
|
||||||
|
<%
|
||||||
|
java.util.List<String> list = org.jboss.resteasy.example.oauth.ProductDatabaseClient.getProducts(request);
|
||||||
|
for (String prod : list)
|
||||||
|
{
|
||||||
|
out.print("<p>");
|
||||||
|
out.print(prod);
|
||||||
|
out.println("</p>");
|
||||||
|
|
||||||
|
}
|
||||||
|
%>
|
||||||
|
<br><br>
|
||||||
|
</body>
|
||||||
|
</html>
|
3
examples/as7-eap-demo/third-party/src/main/webapp/redirect.jsp
vendored
Normal file
3
examples/as7-eap-demo/third-party/src/main/webapp/redirect.jsp
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
<%
|
||||||
|
org.jboss.resteasy.example.oauth.ProductDatabaseClient.redirect(request, response);
|
||||||
|
%>
|
|
@ -38,5 +38,6 @@
|
||||||
<module>as7-eap-demo/customer-app</module>
|
<module>as7-eap-demo/customer-app</module>
|
||||||
<module>as7-eap-demo/product-app</module>
|
<module>as7-eap-demo/product-app</module>
|
||||||
<module>as7-eap-demo/database-service</module>
|
<module>as7-eap-demo/database-service</module>
|
||||||
|
<module>as7-eap-demo/third-party</module>
|
||||||
</modules>
|
</modules>
|
||||||
</project>
|
</project>
|
||||||
|
|
|
@ -17,6 +17,9 @@ import java.util.UUID;
|
||||||
public class AccessCodeEntry {
|
public class AccessCodeEntry {
|
||||||
protected String id = UUID.randomUUID().toString() + System.currentTimeMillis();
|
protected String id = UUID.randomUUID().toString() + System.currentTimeMillis();
|
||||||
protected String code;
|
protected String code;
|
||||||
|
protected String state;
|
||||||
|
protected String redirectUri;
|
||||||
|
|
||||||
protected long expiration;
|
protected long expiration;
|
||||||
protected SkeletonKeyToken token;
|
protected SkeletonKeyToken token;
|
||||||
protected User user;
|
protected User user;
|
||||||
|
@ -79,4 +82,20 @@ public class AccessCodeEntry {
|
||||||
public MultivaluedMap<String, Role> getResourceRolesRequested() {
|
public MultivaluedMap<String, Role> getResourceRolesRequested() {
|
||||||
return resourceRolesRequested;
|
return resourceRolesRequested;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getState() {
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setState(String state) {
|
||||||
|
this.state = state;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRedirectUri() {
|
||||||
|
return redirectUri;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRedirectUri(String redirectUri) {
|
||||||
|
this.redirectUri = redirectUri;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,7 +41,7 @@ import java.util.concurrent.atomic.AtomicLong;
|
||||||
public class RealmManager {
|
public class RealmManager {
|
||||||
private static AtomicLong counter = new AtomicLong(1);
|
private static AtomicLong counter = new AtomicLong(1);
|
||||||
public static final String RESOURCE_ROLE = "KEYCLOAK_RESOURCE";
|
public static final String RESOURCE_ROLE = "KEYCLOAK_RESOURCE";
|
||||||
public static final String OAUTH_CLIENT_ROLE = "KEYCLOAK_OAUTH_CLIENT";
|
public static final String IDENTITY_REQUESTER_ROLE = "KEYCLOAK_IDENTITY_REQUESTER";
|
||||||
public static final String WILDCARD_ROLE = "*";
|
public static final String WILDCARD_ROLE = "*";
|
||||||
|
|
||||||
public static String generateId() {
|
public static String generateId() {
|
||||||
|
@ -78,6 +78,7 @@ public class RealmManager {
|
||||||
RealmModel realm = new RealmModel(newRealm, identitySession);
|
RealmModel realm = new RealmModel(newRealm, identitySession);
|
||||||
idm.add(new SimpleRole(WILDCARD_ROLE));
|
idm.add(new SimpleRole(WILDCARD_ROLE));
|
||||||
idm.add(new SimpleRole(RESOURCE_ROLE));
|
idm.add(new SimpleRole(RESOURCE_ROLE));
|
||||||
|
idm.add(new SimpleRole(IDENTITY_REQUESTER_ROLE));
|
||||||
return realm;
|
return realm;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -54,7 +54,7 @@ public class TokenManager {
|
||||||
return cookie;
|
return cookie;
|
||||||
}
|
}
|
||||||
|
|
||||||
public AccessCodeEntry createAccessCode(String scopeParam, RealmModel realm, User client, User user) {
|
public AccessCodeEntry createAccessCode(String scopeParam, String state, String redirect, RealmModel realm, User client, User user) {
|
||||||
AccessCodeEntry code = new AccessCodeEntry();
|
AccessCodeEntry code = new AccessCodeEntry();
|
||||||
SkeletonKeyScope scopeMap = null;
|
SkeletonKeyScope scopeMap = null;
|
||||||
if (scopeParam != null) scopeMap = decodeScope(scopeParam);
|
if (scopeParam != null) scopeMap = decodeScope(scopeParam);
|
||||||
|
@ -105,6 +105,8 @@ public class TokenManager {
|
||||||
code.setExpiration((System.currentTimeMillis() / 1000) + realm.getAccessCodeLifespan());
|
code.setExpiration((System.currentTimeMillis() / 1000) + realm.getAccessCodeLifespan());
|
||||||
code.setClient(client);
|
code.setClient(client);
|
||||||
code.setUser(user);
|
code.setUser(user);
|
||||||
|
code.setState(state);
|
||||||
|
code.setRedirectUri(redirect);
|
||||||
accessCodeMap.put(code.getId(), code);
|
accessCodeMap.put(code.getId(), code);
|
||||||
String accessCode = null;
|
String accessCode = null;
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -7,9 +7,7 @@ import org.jboss.resteasy.jwt.JsonSerialization;
|
||||||
import org.jboss.resteasy.logging.Logger;
|
import org.jboss.resteasy.logging.Logger;
|
||||||
import org.jboss.resteasy.spi.HttpRequest;
|
import org.jboss.resteasy.spi.HttpRequest;
|
||||||
import org.jboss.resteasy.spi.HttpResponse;
|
import org.jboss.resteasy.spi.HttpResponse;
|
||||||
import org.keycloak.TokenIdGenerator;
|
|
||||||
import org.keycloak.representations.AccessTokenResponse;
|
import org.keycloak.representations.AccessTokenResponse;
|
||||||
import org.keycloak.representations.SkeletonKeyScope;
|
|
||||||
import org.keycloak.representations.SkeletonKeyToken;
|
import org.keycloak.representations.SkeletonKeyToken;
|
||||||
import org.keycloak.services.JspRequestParameters;
|
import org.keycloak.services.JspRequestParameters;
|
||||||
import org.keycloak.services.managers.AccessCodeEntry;
|
import org.keycloak.services.managers.AccessCodeEntry;
|
||||||
|
@ -18,7 +16,6 @@ import org.keycloak.services.managers.RealmManager;
|
||||||
import org.keycloak.services.managers.ResourceAdminManager;
|
import org.keycloak.services.managers.ResourceAdminManager;
|
||||||
import org.keycloak.services.managers.TokenManager;
|
import org.keycloak.services.managers.TokenManager;
|
||||||
import org.keycloak.services.models.RealmModel;
|
import org.keycloak.services.models.RealmModel;
|
||||||
import org.keycloak.services.models.ResourceModel;
|
|
||||||
import org.picketlink.idm.IdentitySession;
|
import org.picketlink.idm.IdentitySession;
|
||||||
import org.picketlink.idm.model.Role;
|
import org.picketlink.idm.model.Role;
|
||||||
import org.picketlink.idm.model.User;
|
import org.picketlink.idm.model.User;
|
||||||
|
@ -31,24 +28,17 @@ import javax.ws.rs.Path;
|
||||||
import javax.ws.rs.Produces;
|
import javax.ws.rs.Produces;
|
||||||
import javax.ws.rs.QueryParam;
|
import javax.ws.rs.QueryParam;
|
||||||
import javax.ws.rs.core.Context;
|
import javax.ws.rs.core.Context;
|
||||||
import javax.ws.rs.core.Cookie;
|
|
||||||
import javax.ws.rs.core.HttpHeaders;
|
import javax.ws.rs.core.HttpHeaders;
|
||||||
import javax.ws.rs.core.MediaType;
|
import javax.ws.rs.core.MediaType;
|
||||||
import javax.ws.rs.core.MultivaluedHashMap;
|
|
||||||
import javax.ws.rs.core.MultivaluedMap;
|
import javax.ws.rs.core.MultivaluedMap;
|
||||||
import javax.ws.rs.core.NewCookie;
|
|
||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
||||||
import javax.ws.rs.core.SecurityContext;
|
import javax.ws.rs.core.SecurityContext;
|
||||||
import javax.ws.rs.core.UriBuilder;
|
import javax.ws.rs.core.UriBuilder;
|
||||||
import javax.ws.rs.core.UriInfo;
|
import javax.ws.rs.core.UriInfo;
|
||||||
import javax.ws.rs.ext.Providers;
|
import javax.ws.rs.ext.Providers;
|
||||||
import java.net.URI;
|
|
||||||
import java.security.PrivateKey;
|
import java.security.PrivateKey;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
@ -77,7 +67,7 @@ public class TokenService {
|
||||||
|
|
||||||
protected String securityFailurePath = "/securityFailure.jsp";
|
protected String securityFailurePath = "/securityFailure.jsp";
|
||||||
protected String loginFormPath = "/loginForm.jsp";
|
protected String loginFormPath = "/loginForm.jsp";
|
||||||
protected String oauthFormPath = "/oauthForm.jsp";
|
protected String oauthFormPath = "/oauthGrantForm.jsp";
|
||||||
|
|
||||||
protected RealmModel realm;
|
protected RealmModel realm;
|
||||||
protected TokenManager tokenManager;
|
protected TokenManager tokenManager;
|
||||||
|
@ -228,17 +218,18 @@ public class TokenService {
|
||||||
|
|
||||||
protected Response processAccessCode(String scopeParam, String state, String redirect, User client, User user) {
|
protected Response processAccessCode(String scopeParam, String state, String redirect, User client, User user) {
|
||||||
Role resourceRole = realm.getIdm().getRole(RealmManager.RESOURCE_ROLE);
|
Role resourceRole = realm.getIdm().getRole(RealmManager.RESOURCE_ROLE);
|
||||||
Role oauthClientRole = realm.getIdm().getRole(RealmManager.OAUTH_CLIENT_ROLE);
|
Role identityRequestRole = realm.getIdm().getRole(RealmManager.IDENTITY_REQUESTER_ROLE);
|
||||||
boolean isResource = realm.getIdm().hasRole(client, resourceRole);
|
boolean isResource = realm.getIdm().hasRole(client, resourceRole);
|
||||||
if (!isResource && !realm.getIdm().hasRole(client, oauthClientRole)) {
|
if (!isResource && !realm.getIdm().hasRole(client, identityRequestRole)) {
|
||||||
securityFailureForward("Login requester not allowed to request login.");
|
securityFailureForward("Login requester not allowed to request login.");
|
||||||
identitySession.close();
|
identitySession.close();
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
AccessCodeEntry accessCode = tokenManager.createAccessCode(scopeParam, realm, client, user);
|
AccessCodeEntry accessCode = tokenManager.createAccessCode(scopeParam, state, redirect, realm, client, user);
|
||||||
|
logger.info("processAccessCode: isResource: " + isResource);
|
||||||
if (!isResource && accessCode.getRealmRolesRequested().size() > 0 && accessCode.getResourceRolesRequested().size() > 0) {
|
logger.info("processAccessCode: go to oauth page?: " + (!isResource && (accessCode.getRealmRolesRequested().size() > 0 || accessCode.getResourceRolesRequested().size() > 0)));
|
||||||
oauthGrantPage(accessCode, client, state, redirect);
|
if (!isResource && (accessCode.getRealmRolesRequested().size() > 0 || accessCode.getResourceRolesRequested().size() > 0)) {
|
||||||
|
oauthGrantPage(accessCode, client);
|
||||||
identitySession.close();
|
identitySession.close();
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -248,6 +239,7 @@ public class TokenService {
|
||||||
protected Response redirectAccessCode(AccessCodeEntry accessCode, String state, String redirect) {
|
protected Response redirectAccessCode(AccessCodeEntry accessCode, String state, String redirect) {
|
||||||
String code = accessCode.getCode();
|
String code = accessCode.getCode();
|
||||||
UriBuilder redirectUri = UriBuilder.fromUri(redirect).queryParam("code", code);
|
UriBuilder redirectUri = UriBuilder.fromUri(redirect).queryParam("code", code);
|
||||||
|
logger.info("redirectAccessCode: state: " + state);
|
||||||
if (state != null) redirectUri.queryParam("state", state);
|
if (state != null) redirectUri.queryParam("state", state);
|
||||||
Response.ResponseBuilder location = Response.status(302).location(redirectUri.build());
|
Response.ResponseBuilder location = Response.status(302).location(redirectUri.build());
|
||||||
if (realm.isCookieLoginAllowed()) {
|
if (realm.isCookieLoginAllowed()) {
|
||||||
|
@ -422,10 +414,11 @@ public class TokenService {
|
||||||
identitySession.close();
|
identitySession.close();
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
Role resourceRole = realm.getIdm().getRole(RealmManager.RESOURCE_ROLE);
|
Role resourceRole = realm.getIdm().getRole(RealmManager.RESOURCE_ROLE);
|
||||||
Role oauthClientRole = realm.getIdm().getRole(RealmManager.OAUTH_CLIENT_ROLE);
|
Role identityRequestRole = realm.getIdm().getRole(RealmManager.IDENTITY_REQUESTER_ROLE);
|
||||||
boolean isResource = realm.getIdm().hasRole(client, resourceRole);
|
boolean isResource = realm.getIdm().hasRole(client, resourceRole);
|
||||||
if (!isResource && !realm.getIdm().hasRole(client, oauthClientRole)) {
|
if (!isResource && !realm.getIdm().hasRole(client, identityRequestRole)) {
|
||||||
securityFailureForward("Login requester not allowed to request login.");
|
securityFailureForward("Login requester not allowed to request login.");
|
||||||
identitySession.close();
|
identitySession.close();
|
||||||
return null;
|
return null;
|
||||||
|
@ -433,6 +426,7 @@ public class TokenService {
|
||||||
|
|
||||||
User user = authManager.authenticateIdentityCookie(realm, uriInfo, headers);
|
User user = authManager.authenticateIdentityCookie(realm, uriInfo, headers);
|
||||||
if (user != null) {
|
if (user != null) {
|
||||||
|
logger.info(user.getLoginName() + " already logged in.");
|
||||||
return processAccessCode(scopeParam, state, redirect, client, user);
|
return processAccessCode(scopeParam, state, redirect, client, user);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -459,13 +453,7 @@ public class TokenService {
|
||||||
@POST
|
@POST
|
||||||
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
|
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
|
||||||
public Response processOAuth(MultivaluedMap<String, String> formData) {
|
public Response processOAuth(MultivaluedMap<String, String> formData) {
|
||||||
String redirect = formData.getFirst("redirect_uri");
|
|
||||||
String state = formData.getFirst("state");
|
|
||||||
if (formData.containsKey("cancel")) {
|
|
||||||
return redirectAccessDenied(redirect, state);
|
|
||||||
}
|
|
||||||
String code = formData.getFirst("code");
|
String code = formData.getFirst("code");
|
||||||
|
|
||||||
JWSInput input = new JWSInput(code, providers);
|
JWSInput input = new JWSInput(code, providers);
|
||||||
boolean verifiedCode = false;
|
boolean verifiedCode = false;
|
||||||
try {
|
try {
|
||||||
|
@ -474,13 +462,25 @@ public class TokenService {
|
||||||
logger.debug("Failed to verify signature", ignored);
|
logger.debug("Failed to verify signature", ignored);
|
||||||
}
|
}
|
||||||
if (!verifiedCode) {
|
if (!verifiedCode) {
|
||||||
return redirectAccessDenied(redirect, state);
|
securityFailureForward("Illegal access code.");
|
||||||
|
identitySession.close();
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
String key = input.readContent(String.class);
|
String key = input.readContent(String.class);
|
||||||
AccessCodeEntry accessCodeEntry = tokenManager.getAccessCode(key);
|
AccessCodeEntry accessCodeEntry = tokenManager.getAccessCode(key);
|
||||||
if (accessCodeEntry == null) {
|
if (accessCodeEntry == null) {
|
||||||
|
securityFailureForward("Unknown access code.");
|
||||||
|
identitySession.close();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
String redirect = accessCodeEntry.getRedirectUri();
|
||||||
|
String state = accessCodeEntry.getState();
|
||||||
|
|
||||||
|
if (formData.containsKey("cancel")) {
|
||||||
return redirectAccessDenied(redirect, state);
|
return redirectAccessDenied(redirect, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
return redirectAccessCode(accessCodeEntry, state, redirect);
|
return redirectAccessCode(accessCodeEntry, state, redirect);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -491,14 +491,12 @@ public class TokenService {
|
||||||
return location.build();
|
return location.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void oauthGrantPage(AccessCodeEntry accessCode, User client, String state, String redirect_uri) {
|
protected void oauthGrantPage(AccessCodeEntry accessCode, User client) {
|
||||||
request.setAttribute("realmRolesRequested", accessCode.getRealmRolesRequested());
|
request.setAttribute("realmRolesRequested", accessCode.getRealmRolesRequested());
|
||||||
request.setAttribute("resourceRolesRequested", accessCode.getResourceRolesRequested());
|
request.setAttribute("resourceRolesRequested", accessCode.getResourceRolesRequested());
|
||||||
request.setAttribute("state", state);
|
|
||||||
request.setAttribute("redirect_uri", redirect_uri);
|
|
||||||
request.setAttribute("client", client);
|
request.setAttribute("client", client);
|
||||||
request.setAttribute("action", processOAuthUrl(uriInfo));
|
request.setAttribute("action", processOAuthUrl(uriInfo).build(realm.getId()).toString());
|
||||||
request.setAttribute("accessCode", accessCode.getCode());
|
request.setAttribute("code", accessCode.getCode());
|
||||||
|
|
||||||
request.forward(oauthFormPath);
|
request.forward(oauthFormPath);
|
||||||
}
|
}
|
||||||
|
|
|
@ -166,7 +166,7 @@ public class AdapterTest {
|
||||||
idm.add(new SimpleRole("admin"));
|
idm.add(new SimpleRole("admin"));
|
||||||
idm.add(new SimpleRole("user"));
|
idm.add(new SimpleRole("user"));
|
||||||
List<Role> roles = realmModel.getRoles();
|
List<Role> roles = realmModel.getRoles();
|
||||||
Assert.assertEquals(4, roles.size());
|
Assert.assertEquals(5, roles.size());
|
||||||
SimpleUser user = new SimpleUser("bburke");
|
SimpleUser user = new SimpleUser("bburke");
|
||||||
idm.add(user);
|
idm.add(user);
|
||||||
Role role = idm.getRole("user");
|
Role role = idm.getRole("user");
|
||||||
|
|
Loading…
Reference in a new issue