Prepare to move to single repository
This commit is contained in:
commit
ac622a2baf
89 changed files with 5886 additions and 0 deletions
9
securing_apps/README.adoc
Executable file
9
securing_apps/README.adoc
Executable file
|
@ -0,0 +1,9 @@
|
||||||
|
|
||||||
|
= Securing Applications and Services Guide
|
||||||
|
|
||||||
|
image:images/keycloak_logo.png[alt="Keycloak"]
|
||||||
|
|
||||||
|
{{book.project.name}} {{book.project.version}}
|
||||||
|
|
||||||
|
http://www.keycloak.org
|
||||||
|
|
91
securing_apps/SUMMARY.adoc
Normal file
91
securing_apps/SUMMARY.adoc
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
= {{book.title}}
|
||||||
|
|
||||||
|
. link:topics/overview/overview.adoc[Overview]
|
||||||
|
.. link:topics/overview/what-are-client-adapters.adoc[What are Client Adapters?]
|
||||||
|
.. link:topics/overview/supported-platforms.adoc[Supported Platforms]
|
||||||
|
.. link:topics/overview/supported-protocols.adoc[Supported Protocols]
|
||||||
|
|
||||||
|
. link:topics/oidc/oidc-overview.adoc[OpenID Connect]
|
||||||
|
|
||||||
|
.. link:topics/oidc/java/java-adapters.adoc[Java Adapters]
|
||||||
|
... link:topics/oidc/java/java-adapter-config.adoc[Java Adapters Config]
|
||||||
|
... link:topics/oidc/java/jboss-adapter.adoc[JBoss EAP/Wildfly Adapter]
|
||||||
|
... link:topics/oidc/java/fuse-adapter.adoc[JBoss Fuse Adapter]
|
||||||
|
.... link:topics/oidc/java/fuse/install-feature.adoc[Install Feature]
|
||||||
|
.... link:topics/oidc/java/fuse/classic-war.adoc[Classic WAR application]
|
||||||
|
.... link:topics/oidc/java/fuse/servlet-whiteboard.adoc[Servlet Deployed as OSGI Service]
|
||||||
|
.... link:topics/oidc/java/fuse/camel.adoc[Apache Camel]
|
||||||
|
.... link:topics/oidc/java/fuse/cxf-separate.adoc[Apache CXF on Separate Jetty]
|
||||||
|
.... link:topics/oidc/java/fuse/cxf-builtin.adoc[Apache CXF on default Jetty]
|
||||||
|
.... link:topics/oidc/java/fuse/fuse-admin.adoc[Fuse Admin Services]
|
||||||
|
.... link:topics/oidc/java/fuse/hawtio.adoc[Hawtio Admin Console]
|
||||||
|
{% if book.community %}
|
||||||
|
... link:topics/oidc/java/tomcat-adapter.adoc[Tomcat 6, 7 and 8 Adapters]
|
||||||
|
... link:topics/oidc/java/jetty9-adapter.adoc[Jetty 9.x Adapters]
|
||||||
|
... link:topics/oidc/java/jetty8-adapter.adoc[Jetty 8.1.x Adapter]
|
||||||
|
... link:topics/oidc/java/spring-boot-adapter.adoc[Spring Boot Adapter]
|
||||||
|
... link:topics/oidc/java/spring-security-adapter.adoc[Spring Security Adapter]
|
||||||
|
{% endif %}
|
||||||
|
{% if book.community %}
|
||||||
|
... link:topics/oidc/java/servlet-filter-adapter.adoc[Java Servlet Filter Adapter]
|
||||||
|
... link:topics/oidc/java/jaas.adoc[JAAS plugin]
|
||||||
|
{% endif %}
|
||||||
|
... link:topics/oidc/java/adapter-context.adoc[Security Context]
|
||||||
|
... link:topics/oidc/java/adapter_error_handling.adoc[Error Handling]
|
||||||
|
... link:topics/oidc/java/logout.adoc[Logout]
|
||||||
|
... link:topics/oidc/java/params_forwarding.adoc[Parameters Forwarding]
|
||||||
|
... link:topics/oidc/java/client-authentication.adoc[Client Authentication]
|
||||||
|
... link:topics/oidc/java/multi-tenancy.adoc[Multi Tenancy]
|
||||||
|
... link:topics/oidc/java/application-clustering.adoc[Application Clustering]
|
||||||
|
|
||||||
|
.. link:topics/oidc/javascript-adapter.adoc[JavaScript Adapter]
|
||||||
|
|
||||||
|
.. link:topics/oidc/nodejs-adapter.adoc[Node.js Adapter]
|
||||||
|
|
||||||
|
.. link:topics/oidc/oidc-generic.adoc[Other OpenID Connect libraries]
|
||||||
|
{% if book.community %}
|
||||||
|
... link:topics/oidc/mod-auth-openidc.adoc[mod_auth_oidc Apache HTTPD Module]
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
. link:topics/saml/saml-overview.adoc[SAML]
|
||||||
|
.. link:topics/saml/java/java-adapters.adoc[Java Adapters]
|
||||||
|
... link:topics/saml/java/general-config.adoc[General Adapter Config]
|
||||||
|
.... link:topics/saml/java/general-config/sp_element.adoc[SP Element]
|
||||||
|
.... link:topics/saml/java/general-config/sp-keys.adoc[SP Keys and Key elements]
|
||||||
|
..... link:topics/saml/java/general-config/sp-keys/keystore_element.adoc[KeyStore Element]
|
||||||
|
..... link:topics/saml/java/general-config/sp-keys/key_pems.adoc[Key PEMS]
|
||||||
|
.... link:topics/saml/java/general-config/sp_principalname_mapping_element.adoc[SP PrincipalNameMapping element]
|
||||||
|
.... link:topics/saml/java/general-config/roleidentifiers_element.adoc[RoleIdentifiers element]
|
||||||
|
.... link:topics/saml/java/general-config/idp_element.adoc[IDP Element]
|
||||||
|
.... link:topics/saml/java/general-config/idp_singlesignonservice_subelement.adoc[IDP SingleSignOnService sub element]
|
||||||
|
.... link:topics/saml/java/general-config/idp_singlelogoutservice_subelement.adoc[IDP SingleLogoutService sub element]
|
||||||
|
.... link:topics/saml/java/general-config/idp_keys_subelement.adoc[IDP Keys subelement]
|
||||||
|
.... link:topics/saml/java/general-config/idp_httpclient_subelement.adoc[IDP HttpClient subelement]
|
||||||
|
... link:topics/saml/java/saml-jboss-adapter.adoc[JBoss EAP/Wildfly Adapter]
|
||||||
|
.... link:topics/saml/java/jboss-adapter/jboss_adapter_installation.adoc[Adapter Installation]
|
||||||
|
.... link:topics/saml/java/jboss-adapter/required_per_war_configuration.adoc[Per WAR Configuration]
|
||||||
|
.... link:topics/saml/java/jboss-adapter/securing_wars.adoc[Securing WARs via SAML Subsystem]
|
||||||
|
{% if book.community %}
|
||||||
|
... link:topics/saml/java/tomcat-adapter.adoc[Tomcat SAML adapters]
|
||||||
|
.... link:topics/saml/java/tomcat-adapter/tomcat_adapter_installation.adoc[Adapter Installation]
|
||||||
|
.... link:topics/saml/java/tomcat-adapter/tomcat_adapter_per_war_config.adoc[Per WAR Configuration]
|
||||||
|
... link:topics/saml/java/jetty-adapter.adoc[Jetty SAML Adapters]
|
||||||
|
.... link:topics/saml/java/jetty-adapter/jetty9_installation.adoc[Jetty 9 Adapter Installation]
|
||||||
|
.... link:topics/saml/java/jetty-adapter/jetty9_per_war_config.adoc[Jetty 9 Per WAR Configuration]
|
||||||
|
.... link:topics/saml/java/jetty-adapter/jetty8-installation.adoc[Jetty 8 Adapter Installation]
|
||||||
|
.... link:topics/saml/java/jetty-adapter/jetty8-per_war_config.adoc[Jetty 8 Per WAR Configuration]
|
||||||
|
{% endif %}
|
||||||
|
{% if book.community %}
|
||||||
|
... link:topics/saml/java/servlet-filter-adapter.adoc[Java Servlet Filter Adapter]
|
||||||
|
{% endif %}
|
||||||
|
... link:topics/saml/java/idp-registration.adoc[Registering with an IDP]
|
||||||
|
... link:topics/saml/java/logout.adoc[Logout]
|
||||||
|
... link:topics/saml/java/assertion-api.adoc[Obtaining Assertion Attributes]
|
||||||
|
... link:topics/saml/java/error_handling.adoc[Error Handling]
|
||||||
|
... link:topics/saml/java/debugging.adoc[Troubleshooting]
|
||||||
|
{% if book.community %}
|
||||||
|
... link:topics/saml/java/MigrationFromOlderVersions.adoc[Migration from older versions]
|
||||||
|
{% endif %}
|
||||||
|
.. link:topics/saml/mod-auth-mellon.adoc[mod_auth_mellon Apache HTTPD Module]
|
||||||
|
. link:topics/client-registration.adoc[Client Registration]
|
||||||
|
.. link:topics/client-registration/client-registration-cli.adoc[Client Registration CLI]
|
29
securing_apps/book-product.json
Executable file
29
securing_apps/book-product.json
Executable file
|
@ -0,0 +1,29 @@
|
||||||
|
{
|
||||||
|
"gitbook": "2.x.x",
|
||||||
|
"structure": {
|
||||||
|
"readme": "README.adoc"
|
||||||
|
},
|
||||||
|
"plugins": [
|
||||||
|
"toggle-chapters",
|
||||||
|
"ungrey",
|
||||||
|
"splitter"
|
||||||
|
],
|
||||||
|
"variables": {
|
||||||
|
"title": "Securing Applications and Services Guide",
|
||||||
|
"project": {
|
||||||
|
"name": "Red Hat Single Sign-On",
|
||||||
|
"version": "7.1.0",
|
||||||
|
"versionMvn": "2.4.0.Final-redhat-1",
|
||||||
|
"doc_base_url": "https://access.redhat.com/documentation/en/red-hat-single-sign-on/",
|
||||||
|
"doc_info_version_url": "7.1-Beta"
|
||||||
|
},
|
||||||
|
"community": false,
|
||||||
|
"product": true,
|
||||||
|
"images": "rhsso-images",
|
||||||
|
"adminguide": {
|
||||||
|
"name": "Server Administration Guide",
|
||||||
|
"link": "/single/server-administration-guide/"
|
||||||
|
},
|
||||||
|
"fuseVersion": "JBoss Fuse 6.3.0 Rollup 1"
|
||||||
|
}
|
||||||
|
}
|
35
securing_apps/book.json
Executable file
35
securing_apps/book.json
Executable file
|
@ -0,0 +1,35 @@
|
||||||
|
{
|
||||||
|
"gitbook": "2.x.x",
|
||||||
|
"structure": {
|
||||||
|
"readme": "README.adoc"
|
||||||
|
},
|
||||||
|
"plugins": [
|
||||||
|
"toggle-chapters",
|
||||||
|
"ungrey",
|
||||||
|
"splitter"
|
||||||
|
],
|
||||||
|
"variables": {
|
||||||
|
"title": "Securing Applications and Services Guide",
|
||||||
|
"project": {
|
||||||
|
"name": "Keycloak",
|
||||||
|
"version": "SNAPSHOT",
|
||||||
|
"versionMvn": "SNAPSHOT"
|
||||||
|
},
|
||||||
|
"community": true,
|
||||||
|
"product": false,
|
||||||
|
"images": "keycloak-images",
|
||||||
|
"developerguide": {
|
||||||
|
"name": "Server Developer Guide",
|
||||||
|
"link": "https://keycloak.gitbooks.io/server-developer-guide/content/"
|
||||||
|
},
|
||||||
|
"adminguide": {
|
||||||
|
"name": "Server Administration Guide",
|
||||||
|
"link": "https://keycloak.gitbooks.io/server-adminstration-guide/content/"
|
||||||
|
},
|
||||||
|
"installguide": {
|
||||||
|
"name": "Server Installation and Configuration Guide",
|
||||||
|
"link": "https://keycloak.gitbooks.io/server-installation-and-configuration/content/"
|
||||||
|
},
|
||||||
|
"fuseVersion": "JBoss Fuse 6.3.0 Rollup 1"
|
||||||
|
}
|
||||||
|
}
|
7
securing_apps/build.sh
Executable file
7
securing_apps/build.sh
Executable file
|
@ -0,0 +1,7 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
cd $(readlink -f `dirname $0`)
|
||||||
|
|
||||||
|
python gitlab-conversion.py
|
||||||
|
cd target
|
||||||
|
asciidoctor master.adoc
|
69
securing_apps/buildGuide.sh
Executable file
69
securing_apps/buildGuide.sh
Executable file
|
@ -0,0 +1,69 @@
|
||||||
|
# Build the guide
|
||||||
|
|
||||||
|
# Find the directory name and full path
|
||||||
|
CURRENT_GUIDE=${PWD##*/}
|
||||||
|
CURRENT_DIRECTORY=$(pwd)
|
||||||
|
|
||||||
|
usage(){
|
||||||
|
cat <<EOM
|
||||||
|
USAGE: $0 [OPTION]
|
||||||
|
|
||||||
|
DESCRIPTION: Build the documentation in this directory.
|
||||||
|
|
||||||
|
OPTIONS:
|
||||||
|
-h Print help.
|
||||||
|
|
||||||
|
EOM
|
||||||
|
}
|
||||||
|
|
||||||
|
while getopts "ht:" c
|
||||||
|
do
|
||||||
|
case "$c" in
|
||||||
|
h) usage
|
||||||
|
exit 1;;
|
||||||
|
\?) echo "Unknown option: -$OPTARG." >&2
|
||||||
|
usage
|
||||||
|
exit 1;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ ! -d target ]; then
|
||||||
|
echo "You must run 'python gitlab-conversion.py' to convert the content before you run this script."
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Remove the html and build directories and then recreate the html/images/ directory
|
||||||
|
if [ -d target/html ]; then
|
||||||
|
- rm -r target/html/
|
||||||
|
fi
|
||||||
|
if [ -d target/html ]; then
|
||||||
|
rm -r target/html/
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p html
|
||||||
|
cp -r target/images/ target/html/
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "********************************************"
|
||||||
|
echo " Building $CURRENT_GUIDE "
|
||||||
|
echo "********************************************"
|
||||||
|
echo ""
|
||||||
|
echo "Building an asciidoctor version of the guide"
|
||||||
|
asciidoctor -t -dbook -a toc -o target/html/$CURRENT_GUIDE.html target/master.adoc
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Building a ccutil version of the guide"
|
||||||
|
ccutil compile --lang en_US --format html-single --main-file target/master.adoc
|
||||||
|
|
||||||
|
cd ..
|
||||||
|
|
||||||
|
echo "View the asciidoctor build here: " file://$CURRENT_DIRECTORY/target/html/$CURRENT_GUIDE.html
|
||||||
|
|
||||||
|
if [ -d $CURRENT_DIRECTORY/build/tmp/en-US/html-single/ ]; then
|
||||||
|
echo "View the ccutil build here: " file://$CURRENT_DIRECTORY/build/tmp/en-US/html-single/index.html
|
||||||
|
exit 0
|
||||||
|
else
|
||||||
|
echo -e "${RED}Build using ccutil failed!"
|
||||||
|
echo -e "${BLACK}See the log above for details."
|
||||||
|
exit 1
|
||||||
|
fi
|
113
securing_apps/gitlab-conversion.py
Executable file
113
securing_apps/gitlab-conversion.py
Executable file
|
@ -0,0 +1,113 @@
|
||||||
|
import sys, os, re, json, shutil, errno
|
||||||
|
|
||||||
|
def transform(root, f, targetdir):
|
||||||
|
full = os.path.join(root, f)
|
||||||
|
input = open(full, 'r').read()
|
||||||
|
dir = os.path.join(targetdir, root)
|
||||||
|
if not os.path.exists(dir):
|
||||||
|
os.makedirs(dir)
|
||||||
|
output = open(os.path.join(dir, f), 'w')
|
||||||
|
input = applyTransformation(input)
|
||||||
|
output.write(input)
|
||||||
|
|
||||||
|
|
||||||
|
def applyTransformation(input):
|
||||||
|
for variable in re.findall(r"\{\{(.*?)\}\}", input):
|
||||||
|
tmp = variable.replace('.', '_')
|
||||||
|
input = input.replace(variable, tmp)
|
||||||
|
input = input.replace('{{', '{').replace('}}', '}')
|
||||||
|
input = re.sub(r"<<fake.+#", "<<", input)
|
||||||
|
for variable in re.findall(r"[ ]*{% if (.*?) %}", input):
|
||||||
|
tmp = variable.replace('.', '_')
|
||||||
|
input = input.replace(variable, tmp)
|
||||||
|
exp = re.compile("[ ]*{% if (.*?) %}(.*?)[ ]*{% endif %}", re.DOTALL)
|
||||||
|
input = re.sub(exp, "ifeval::[{\g<1>}==true]\g<2>endif::[]", input)
|
||||||
|
input = re.sub(r"image:(\.\./)*", "image:", input)
|
||||||
|
input = re.sub(r"image::(\.\./)*", "image::", input)
|
||||||
|
return input
|
||||||
|
|
||||||
|
|
||||||
|
indir = 'topics'
|
||||||
|
targetdir = 'target'
|
||||||
|
if len(sys.argv) > 1:
|
||||||
|
targetdir = sys.argv[1]
|
||||||
|
|
||||||
|
if os.path.exists(targetdir):
|
||||||
|
shutil.rmtree(targetdir)
|
||||||
|
|
||||||
|
if os.path.isdir('images'):
|
||||||
|
shutil.copytree('images',os.path.join(targetdir, 'images'))
|
||||||
|
if os.path.isdir('keycloak-images'):
|
||||||
|
shutil.copytree('keycloak-images',os.path.join(targetdir, 'keycloak-images'))
|
||||||
|
if os.path.isdir('rhsso-images'):
|
||||||
|
shutil.copytree('rhsso-images',os.path.join(targetdir, 'rhsso-images'))
|
||||||
|
|
||||||
|
shutil.copyfile('metadata.ini', os.path.join(targetdir, 'metadata.ini'));
|
||||||
|
shutil.copyfile('master-docinfo.xml', os.path.join(targetdir, 'master-docinfo.xml'));
|
||||||
|
|
||||||
|
tmp = os.path.join(targetdir, 'topics')
|
||||||
|
if not os.path.exists(tmp):
|
||||||
|
os.makedirs(tmp)
|
||||||
|
|
||||||
|
# transform files
|
||||||
|
for root, dirs, filenames in os.walk(indir):
|
||||||
|
for f in filenames:
|
||||||
|
transform(root,f,targetdir)
|
||||||
|
|
||||||
|
# Create master.doc includes
|
||||||
|
input = open('SUMMARY.adoc', 'r').read()
|
||||||
|
output = open(os.path.join(targetdir, 'master.adoc'), 'w')
|
||||||
|
|
||||||
|
output.write("""
|
||||||
|
:toc:
|
||||||
|
:toclevels: 3
|
||||||
|
:numbered:
|
||||||
|
|
||||||
|
include::document-attributes.adoc[]
|
||||||
|
""")
|
||||||
|
|
||||||
|
input = re.sub(r"[ ]*\.+\s*link:(.*)\[(.*)\]", "include::\g<1>[]", input)
|
||||||
|
input = applyTransformation(input)
|
||||||
|
output.write(input)
|
||||||
|
|
||||||
|
# parse book-product.json file and create document attributes
|
||||||
|
with open('book-product.json') as data_file:
|
||||||
|
data = json.load(data_file)
|
||||||
|
|
||||||
|
variables = data['variables']
|
||||||
|
|
||||||
|
def makeAttributes(variables, variable, list):
|
||||||
|
for i in variables.keys():
|
||||||
|
if variable is None:
|
||||||
|
tmp = i
|
||||||
|
else:
|
||||||
|
tmp = variable + '_' + i
|
||||||
|
if isinstance(variables[i],dict):
|
||||||
|
makeAttributes(variables[i], tmp, list)
|
||||||
|
elif isinstance(variables[i],bool):
|
||||||
|
boolval = 'false'
|
||||||
|
if variables[i]:
|
||||||
|
boolval = 'true'
|
||||||
|
list.append({tmp: boolval})
|
||||||
|
else:
|
||||||
|
list.append({tmp: str(variables[i])})
|
||||||
|
|
||||||
|
|
||||||
|
attributeList = []
|
||||||
|
makeAttributes(variables, None, attributeList)
|
||||||
|
|
||||||
|
output = open(os.path.join(targetdir, 'document-attributes.adoc'), 'w')
|
||||||
|
for attribute in attributeList:
|
||||||
|
for k in attribute.keys():
|
||||||
|
output.write(':book_' + k + ": " + attribute[k] + "\n")
|
||||||
|
|
||||||
|
print "Transformation complete!"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
BIN
securing_apps/images/keycloak_logo.png
Executable file
BIN
securing_apps/images/keycloak_logo.png
Executable file
Binary file not shown.
After Width: | Height: | Size: 18 KiB |
3
securing_apps/internal-resources/README.adoc
Normal file
3
securing_apps/internal-resources/README.adoc
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
= Documentation : internal-resources/
|
||||||
|
|
||||||
|
The `internal-resources/` directory contains internal reference documentation written for use by the documentation team.
|
17
securing_apps/internal-resources/template_concept.adoc
Normal file
17
securing_apps/internal-resources/template_concept.adoc
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
[[concept_module]]
|
||||||
|
|
||||||
|
= Concept Template and Guidelines
|
||||||
|
|
||||||
|
_In the title, include nouns that are used in the body text -- this helps readers and search engines find information quickly._
|
||||||
|
|
||||||
|
A concept module describes and explains things such as a product, subsystem, or feature -- what a customer needs to understand to do a task. A concept module may also explain how things relate and interact with other things. The use of graphics and diagrams can speed up understanding of a concept.
|
||||||
|
|
||||||
|
* Look at nouns and noun phrases in related task modules and user story assemblies to find the concepts to explain to users.
|
||||||
|
|
||||||
|
* A concept module in product documentation should explain only things that are visible to users.
|
||||||
|
|
||||||
|
* If a product concept is interesting, but not visible to users, the concept probably does not require explanation in a concept module.
|
||||||
|
|
||||||
|
* A concept module should NOT include numbered steps or other wording that instructs a user to execute a command or perform an action. Instead, put that information in a separate task module or user story assembly.
|
||||||
|
|
||||||
|
|
13
securing_apps/internal-resources/template_reference.adoc
Normal file
13
securing_apps/internal-resources/template_reference.adoc
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
[[reference_module]]
|
||||||
|
|
||||||
|
= Reference Template and Guidelines
|
||||||
|
|
||||||
|
_In the title, include nouns that are used in the body text — this helps readers and search engines find information quickly._
|
||||||
|
|
||||||
|
A reference module lists things (such as a list of commands) or has a very regimented structure (such as the consistent structure of man pages). A reference module explains the details that a customer needs to know to do a task. A reference module is well-organized if users can scan it to quickly find the details they want.
|
||||||
|
|
||||||
|
* A reference module that is a list of things may be made easier to scan if its content is organized alphabetically or formatted as a table. Think of an alphabetical list of commands that can be used with an application, or of an alphabetical list of system components with brief definitions formatted as a 2-column table.
|
||||||
|
|
||||||
|
* If you have a large volume of the same type of information to document, figure out a consistent structure that the information details can fit into and then doument each logical unit of information as 1 reference module. Think of man pages, which document very different information details, but that use consistent titles and formats to present those details in a consistent information structure.
|
||||||
|
|
||||||
|
|
59
securing_apps/internal-resources/template_task.adoc
Normal file
59
securing_apps/internal-resources/template_task.adoc
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
[[task_module]]
|
||||||
|
|
||||||
|
= Do One Task
|
||||||
|
|
||||||
|
_Start title with verb form such as Creating. Use the gerund form (noun form of verb) for titles, not the imperative form._
|
||||||
|
|
||||||
|
_Avoid using the word 'Configuring' over and over. Other words might include:_
|
||||||
|
|
||||||
|
* _Specifying_
|
||||||
|
* _Adding_
|
||||||
|
* _Constructing_
|
||||||
|
* _Arranging_
|
||||||
|
* _Building_
|
||||||
|
* _Setting_
|
||||||
|
* _Managing_
|
||||||
|
* _Defining_
|
||||||
|
* _Customizing_
|
||||||
|
* _Or instead of using synonyms (which becomes obvious) 'verb-alize' a word and refactor the heading - limiting (instead of 'setting resource limits')_
|
||||||
|
|
||||||
|
_The standard for all Red Hat documentation is title case for all headings and titles._
|
||||||
|
|
||||||
|
_A task module is a procedure written with numbered steps -- what a customer needs to do to accomplish a goal successfully._
|
||||||
|
|
||||||
|
This paragraph explains why the user performs the task, sets the context of the task, and may explain or list special considerations specific to this task. Keep the information brief and focused on what is needed for this specific task. Suggested length is 1 to 3 sentences, can be longer if needed.
|
||||||
|
|
||||||
|
|
||||||
|
.Prerequisites _(if needed)_
|
||||||
|
|
||||||
|
* Sentence or a bulleted list of prerequisites that must be in place or done before the user starts this task.
|
||||||
|
|
||||||
|
* Delete section title and bullets if the task has no required prerequisites.
|
||||||
|
|
||||||
|
* Text can be a link to a prerequisite task that the user must do before starting this task.
|
||||||
|
|
||||||
|
|
||||||
|
.Procedure
|
||||||
|
|
||||||
|
_Start title with verb form such as Creating. Use the gerund form (noun form of verb) for titles, not the imperative form._
|
||||||
|
|
||||||
|
_Put steps in a numbered list. The step sequence is important to a repeatable successful outcome._
|
||||||
|
|
||||||
|
. Start each step with an active verb, because each step corresponds to one user action.
|
||||||
|
|
||||||
|
. Include one command or action per step.
|
||||||
|
|
||||||
|
. Format the step line as an unnumbered bullet if the procedure includes only 1 step (exception to numbering the steps).
|
||||||
|
|
||||||
|
. Include one command or action per step.
|
||||||
|
|
||||||
|
. Include one command or action per step.
|
||||||
|
|
||||||
|
|
||||||
|
.Related Information _(if needed)_
|
||||||
|
|
||||||
|
* Bulleted list of links to concepts, reference, or other tasks closely related to this task.
|
||||||
|
|
||||||
|
* Include only the most relevant items as links, not every possible related item.
|
||||||
|
|
||||||
|
* Delete section title and bullets if no related information is needed.
|
20
securing_apps/internal-resources/template_task_brief.adoc
Normal file
20
securing_apps/internal-resources/template_task_brief.adoc
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
[[task_module]]
|
||||||
|
|
||||||
|
= Do One Task
|
||||||
|
|
||||||
|
This paragraph explains why the user performs the task, sets the context of the task, and may explain or list special considerations specific to this task. Keep the information brief and focused on what is needed for this specific task. Suggested length is 1 to 3 sentences, can be longer if needed.
|
||||||
|
|
||||||
|
|
||||||
|
.Prerequisites _(if needed)_
|
||||||
|
|
||||||
|
* List
|
||||||
|
|
||||||
|
|
||||||
|
.Procedure
|
||||||
|
|
||||||
|
. List
|
||||||
|
|
||||||
|
|
||||||
|
.Related Information _(if needed)_
|
||||||
|
|
||||||
|
* List
|
Binary file not shown.
After Width: | Height: | Size: 252 KiB |
12
securing_apps/master-docinfo.xml
Executable file
12
securing_apps/master-docinfo.xml
Executable file
|
@ -0,0 +1,12 @@
|
||||||
|
<productname>{book_project_name}</productname>
|
||||||
|
<productnumber>{book_project_doc_info_version_url}</productnumber>
|
||||||
|
<subtitle>For Use with {book_project_name} {book_project_doc_info_version_url}</subtitle>
|
||||||
|
<title>{book_title}</title>
|
||||||
|
<release>{doc_info_version_url}</release>
|
||||||
|
<abstract>
|
||||||
|
<para>This guide consists of information for securing applications and services using {book_project_name} {book_project_doc_info_version_url}</para>
|
||||||
|
</abstract>
|
||||||
|
<authorgroup>
|
||||||
|
<orgname>Red Hat Customer Content Services</orgname>
|
||||||
|
</authorgroup>
|
||||||
|
<xi:include href="Common_Content/Legal_Notice.xml" xmlns:xi="http://www.w3.org/2001/XInclude" />
|
20
securing_apps/metadata.ini
Normal file
20
securing_apps/metadata.ini
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
[source]
|
||||||
|
language = en-US
|
||||||
|
type = book
|
||||||
|
markup = asciidoc
|
||||||
|
|
||||||
|
[metadata]
|
||||||
|
title = Securing Applications and Services Guide
|
||||||
|
product = Red Hat Single Sign-On
|
||||||
|
version = 7.0
|
||||||
|
edition =
|
||||||
|
subtitle =
|
||||||
|
keywords =
|
||||||
|
abstract =
|
||||||
|
|
||||||
|
[bugs]
|
||||||
|
reporting_url =
|
||||||
|
type =
|
||||||
|
product =
|
||||||
|
component = Documentation
|
||||||
|
|
BIN
securing_apps/rhsso-images/mod-auth-mellon-config-download.png
Normal file
BIN
securing_apps/rhsso-images/mod-auth-mellon-config-download.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 248 KiB |
208
securing_apps/topics/client-registration.adoc
Normal file
208
securing_apps/topics/client-registration.adoc
Normal file
|
@ -0,0 +1,208 @@
|
||||||
|
[[_client_registration]]
|
||||||
|
== Client Registration
|
||||||
|
|
||||||
|
In order for an application or service to utilize {{book.project.name}} it has to register a client in {{book.project.name}}.
|
||||||
|
An admin can do this through the admin console (or admin REST endpoints), but clients can also register themselves through the {{book.project.name}} client
|
||||||
|
registration service.
|
||||||
|
|
||||||
|
The Client Registration Service provides built-in support for {{book.project.name}} Client Representations, OpenID Connect Client Meta Data and SAML Entity Descriptors.
|
||||||
|
The Client Registration Service endpoint is `/realms/<realm>/clients-registrations/<provider>`.
|
||||||
|
|
||||||
|
The built-in supported `providers` are:
|
||||||
|
|
||||||
|
* default - {{book.project.name}} Client Representation (JSON)
|
||||||
|
* install - {{book.project.name}} Adapter Configuration (JSON)
|
||||||
|
* openid-connect - OpenID Connect Client Metadata Description (JSON)
|
||||||
|
* saml2-entity-descriptor - SAML Entity Descriptor (XML)
|
||||||
|
|
||||||
|
The following sections will describe how to use the different providers.
|
||||||
|
|
||||||
|
=== Authentication
|
||||||
|
|
||||||
|
To invoke the Client Registration Services you usually need a token. The token can be a bearer token, an initial access token or a registration access token.
|
||||||
|
There is an alternative to register new client without any token as well, but then you need to configure Client Registration Policies (see below).
|
||||||
|
|
||||||
|
==== Bearer Token
|
||||||
|
|
||||||
|
The bearer token can be issued on behalf of a user or a Service Account. The following permissions are required to invoke the endpoints (see link:{{book.project.doc_base_url}}{{book.project.doc_info_version_url}}{{book.adminguide.link}}[{{book.adminguide.name}}] for more details):
|
||||||
|
|
||||||
|
* create-client or manage-client - To create clients
|
||||||
|
* view-client or manage-client - To view clients
|
||||||
|
* manage-client - To update or delete client
|
||||||
|
|
||||||
|
If you are using a bearer token to create clients it's recommend to use a token from a Service Account with only the `create-client` role (see link:{{book.project.doc_base_url}}{{book.project.doc_info_version_url}}{{book.adminguide.link}}[{{book.adminguide.name}}] for more details).
|
||||||
|
|
||||||
|
==== Initial Access Token
|
||||||
|
|
||||||
|
The recommended approach to registering new clients is by using initial access tokens.
|
||||||
|
An initial access token can only be used to create clients and has a configurable expiration as well as a configurable limit on how many clients can be created.
|
||||||
|
|
||||||
|
An initial access token can be created through the admin console.
|
||||||
|
To create a new initial access token first select the realm in the admin console, then click on `Realm Settings` in the menu on the left, followed by
|
||||||
|
`Client Registration` in the tabs displayed in the page. Then finally click on `Initial Access Tokens` sub-tab.
|
||||||
|
|
||||||
|
You will now be able to see any existing initial access tokens. If you have access you can delete tokens that are no longer required. You can only retrieve the
|
||||||
|
value of the token when you are creating it. To create a new token click on `Create`. You can now optionally add how long the token should be valid, also how
|
||||||
|
many clients can be created using the token. After you click on `Save` the token value is displayed.
|
||||||
|
|
||||||
|
It is important that you copy/paste this token now as you won't be able to retrieve it later. If you forget to copy/paste it, then delete the token and create another one.
|
||||||
|
|
||||||
|
The token value is used as a standard bearer token when invoking the Client Registration Services, by adding it to the Authorization header in the request.
|
||||||
|
For example:
|
||||||
|
|
||||||
|
[source]
|
||||||
|
----
|
||||||
|
Authorization: bearer eyJhbGciOiJSUz...
|
||||||
|
----
|
||||||
|
|
||||||
|
==== Registration Access Token
|
||||||
|
|
||||||
|
When you create a client through the Client Registration Service the response will include a registration access token.
|
||||||
|
The registration access token provides access to retrieve the client configuration later, but also to update or delete the client.
|
||||||
|
The registration access token is included with the request in the same way as a bearer token or initial access token.
|
||||||
|
Registration access tokens are only valid once when it's used the response will include a new token.
|
||||||
|
|
||||||
|
If a client was created outside of the Client Registration Service it won't have a registration access token associated with it.
|
||||||
|
You can create one through the admin console. This can also be useful if you loose the token for a particular client.
|
||||||
|
To create a new token find the client in the admin console and click on `Credentials`. Then click on `Generate registration access token`.
|
||||||
|
|
||||||
|
=== {{book.project.name}} Representations
|
||||||
|
|
||||||
|
The `default` client registration provider can be used to create, retrieve, update and delete a client.
|
||||||
|
It uses {{book.project.name}} Client Representation format which provides support for configuring clients exactly as they can be configured through the admin
|
||||||
|
console, including for example configuring protocol mappers.
|
||||||
|
|
||||||
|
To create a client create a Client Representation (JSON) then do a HTTP POST to `/realms/<realm>/clients-registrations/default`.
|
||||||
|
|
||||||
|
It will return a Client Representation that also includes the registration access token.
|
||||||
|
You should save the registration access token somewhere if you want to retrieve the config, update or delete the client later.
|
||||||
|
|
||||||
|
To retrieve the Client Representation then do a HTTP GET to `/realms/<realm>/clients-registrations/default/<client id>`.
|
||||||
|
|
||||||
|
It will also return a new registration access token.
|
||||||
|
|
||||||
|
To update the Client Representation then do a HTTP PUT to with the updated Client Representation to:
|
||||||
|
`/realms/<realm>/clients-registrations/default/<client id>`.
|
||||||
|
|
||||||
|
It will also return a new registration access token.
|
||||||
|
|
||||||
|
To delete the Client Representation then do a HTTP DELETE to:
|
||||||
|
`/realms/<realm>/clients-registrations/default/<client id>`
|
||||||
|
|
||||||
|
=== {{book.project.name}} Adapter Configuration
|
||||||
|
|
||||||
|
The `installation` client registration provider can be used to retrieve the adapter configuration for a client.
|
||||||
|
In addition to token authentication you can also authenticate with client credentials using HTTP basic authentication.
|
||||||
|
To do this include the following header in the request:
|
||||||
|
|
||||||
|
[source]
|
||||||
|
----
|
||||||
|
Authorization: basic BASE64(client-id + ':' + client-secret)
|
||||||
|
----
|
||||||
|
|
||||||
|
To retrieve the Adapter Configuration then do a HTTP GET to `/realms/<realm>/clients-registrations/install/<client id>`.
|
||||||
|
|
||||||
|
No authentication is required for public clients.
|
||||||
|
This means that for the JavaScript adapter you can load the client configuration directly from {{book.project.name}} using the above URL.
|
||||||
|
|
||||||
|
=== OpenID Connect Dynamic Client Registration
|
||||||
|
|
||||||
|
{{book.project.name}} implements https://openid.net/specs/openid-connect-registration-1_0.html[OpenID Connect Dynamic Client Registration], which extends https://tools.ietf.org/html/rfc7591[OAuth 2.0 Dynamic Client Registration Protocol] and https://tools.ietf.org/html/rfc7592[OAuth 2.0 Dynamic Client Registration Management Protocol].
|
||||||
|
|
||||||
|
The endpoint to use these specifications to register clients in {{book.project.name}} is `/realms/<realm>/clients-registrations/openid-connect[/<client id>]`.
|
||||||
|
|
||||||
|
This endpoints can also be found in the OpenID Connect Discovery endpoint for the realm, `/realms/<realm>/.well-known/openid-configuration`.
|
||||||
|
|
||||||
|
=== SAML Entity Descriptors
|
||||||
|
|
||||||
|
The SAML Entity Descriptor endpoint only supports using SAML v2 Entity Descriptors to create clients.
|
||||||
|
It doesn't support retrieving, updating or deleting clients.
|
||||||
|
For those operations the {{book.project.name}} representation endpoints should be used.
|
||||||
|
When creating a client a {{book.project.name}} Client Representation is returned with details about the created client, including a registration access token.
|
||||||
|
|
||||||
|
To create a client do a HTTP POST with the SAML Entity Descriptor to `/realms/<realm>/clients-registrations/saml2-entity-descriptor`.
|
||||||
|
|
||||||
|
=== Example using CURL
|
||||||
|
|
||||||
|
The following example creates a client with the clientId `myclient` using CURL. You need to replace `eyJhbGciOiJSUz...` with a proper initial access token or
|
||||||
|
bearer token.
|
||||||
|
|
||||||
|
[source,bash]
|
||||||
|
----
|
||||||
|
curl -X POST \
|
||||||
|
-d '{ "clientId": "myclient" }' \
|
||||||
|
-H "Content-Type:application/json" \
|
||||||
|
-H "Authorization: bearer eyJhbGciOiJSUz..." \
|
||||||
|
http://localhost:8080/auth/realms/master/clients-registrations/default
|
||||||
|
----
|
||||||
|
|
||||||
|
=== Example using Java Client Registration API
|
||||||
|
|
||||||
|
The Client Registration Java API makes it easy to use the Client Registration Service using Java.
|
||||||
|
To use include the dependency `org.keycloak:keycloak-client-registration-api:>VERSION<` from Maven.
|
||||||
|
|
||||||
|
For full instructions on using the Client Registration refer to the JavaDocs.
|
||||||
|
Below is an example of creating a client. You need to replace `eyJhbGciOiJSUz...` with a proper initial access token or bearer token.
|
||||||
|
|
||||||
|
[source,java]
|
||||||
|
----
|
||||||
|
String token = "eyJhbGciOiJSUz...";
|
||||||
|
|
||||||
|
ClientRepresentation client = new ClientRepresentation();
|
||||||
|
client.setClientId(CLIENT_ID);
|
||||||
|
|
||||||
|
ClientRegistration reg = ClientRegistration.create()
|
||||||
|
.url("http://localhost:8080/auth", "myrealm")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
reg.auth(Auth.token(token));
|
||||||
|
|
||||||
|
client = reg.create(client);
|
||||||
|
|
||||||
|
String registrationAccessToken = client.getRegistrationAccessToken();
|
||||||
|
----
|
||||||
|
|
||||||
|
=== Client Registration Policies
|
||||||
|
|
||||||
|
{{book.project.name}} currently supports 2 ways how can be new clients registered through Client Registration Service.
|
||||||
|
|
||||||
|
* Authenticated requests - Request to register new client must contain either `Initial Access Token` or `Bearer Token` as mentioned above.
|
||||||
|
|
||||||
|
* Anonymous requests - Request to register new client doesn't need to contain any token at all
|
||||||
|
|
||||||
|
Anonymous client registration requests are very interesting and powerful feature, however you usually don't want that anyone is able to register new
|
||||||
|
client without any limitations. Hence we have `Client Registration Policy SPI`, which provide a way to limit who can register new clients and under which conditions.
|
||||||
|
|
||||||
|
In {{book.project.name}} admin console, you can click to `Client Registration` tab and then `Client Registration Policies` sub-tab. Here you will see what policies
|
||||||
|
are configured by default for anonymous requests and what policies are configured for authenticated requests.
|
||||||
|
|
||||||
|
NOTE: The anonymous requests (requests without any token) are allowed just for creating (registration) of new clients. So when you register
|
||||||
|
new client through anonymous request, the response will contain Registration Access Token, which must be used for Read, Update or Delete request of particular client.
|
||||||
|
However using this Registration Access Token from anonymous registration will be then subject to Anonymous Policy too! This means that for example request for update
|
||||||
|
client also needs to come from Trusted Host if you have `Trusted Hosts` policy. Also for example it won't be allowed to disable `Consent Required` when updating client and
|
||||||
|
when `Consent Required` policy is present etc.
|
||||||
|
|
||||||
|
Currently we have these policy implementations:
|
||||||
|
|
||||||
|
* Trusted Hosts Policy - You can configure list of trusted hosts and trusted domains. Request to Client Registration Service can be sent just from those hosts or domains.
|
||||||
|
Request sent from some untrusted IP will be rejected. URLs of newly registered client must also use just those trusted hosts or domains. For example it won't be allowed
|
||||||
|
to set `Redirect URI` of client pointing to some untrusted host. By default, there is not any whitelisted host, so anonymous client registration is de-facto disabled by default.
|
||||||
|
|
||||||
|
* Consent Required Policy - Newly registered clients will have `Consent Allowed` switch enabled. So after successful authentication, user will always
|
||||||
|
see consent screen when he needs to approve personal info and permissions (protocol mappers and roles). It means that client won't have access to any personal
|
||||||
|
info or permission of user unless user approves it.
|
||||||
|
|
||||||
|
* Protocol Mappers Policy - Allows to configure list of whitelisted protocol mapper implementations. New client can't be registered
|
||||||
|
or updated if it contains some non-whitelisted protocol mapper. Note that this policy is used for authenticated requests as well, so
|
||||||
|
even for authenticated request there are some limitations which protocol mappers can be used.
|
||||||
|
|
||||||
|
* Client Template Policy - Allow to whitelist `Client Templates`, which can be used with newly registered or updated clients.
|
||||||
|
There are no whitelisted templates by default.
|
||||||
|
|
||||||
|
* Full Scope Policy - Newly registered clients will have `Full Scope Allowed` switch disabled. This means they won't have any scoped
|
||||||
|
realm roles or client roles of other clients.
|
||||||
|
|
||||||
|
* Max Clients Policy - Rejects registration if current number of clients in the realm is same or bigger than specified limit. It's 200 by default for anonymous registrations.
|
||||||
|
|
||||||
|
* Client Disabled Policy - Newly registered client will be disabled. This means that admin needs to manually approve and enable all newly registered clients.
|
||||||
|
This policy is not used by default even for anonymous registration.
|
|
@ -0,0 +1,390 @@
|
||||||
|
[[_client_registration_cli]]
|
||||||
|
== Client Registration CLI
|
||||||
|
|
||||||
|
{% if book.product %}
|
||||||
|
NOTE: Client Registration CLI is a Technology Preview feature and is not fully supported.
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
`Client Registration CLI` is a command line interface tool that can be used by application developers to configure new clients
|
||||||
|
to integrate with {book_project_name}. It is specifically designed to interact with {{book.project.name}} Client Registration REST endpoints.
|
||||||
|
|
||||||
|
It is necessary to create a new client configuration for each new application hosted on a unique hostname in order for Keycloak
|
||||||
|
to be able to interact with the application (and vice-versa) and perform its function of providing a login page, SSO session management etc.
|
||||||
|
|
||||||
|
`Client Registration CLI` allows you to configure application clients from a command line, and can be used in shell scripts as well.
|
||||||
|
|
||||||
|
To allow a particular user to use `Client Registration CLI` a {book_project_name} administrator will typically use `Admin Console` to configure
|
||||||
|
a new user, or configure a new client, and a client secret, to protect access to `Client Registration REST API`.
|
||||||
|
|
||||||
|
|
||||||
|
[[_configuring_a_user_for_client_registration_cli]]
|
||||||
|
=== Configuring a new regular user for use with Client Registration CLI
|
||||||
|
|
||||||
|
Login as `admin` into `Admin Console` (e.g. `http://localhost:8080/auth/admin`). Select a realm you want to administer.
|
||||||
|
If you want to use existing user, select it for edit, otherwise create a new user. Go to `Role Mappings` tab. Under
|
||||||
|
`Client Roles` select `realm-management`. Under `Available Roles` select `manage-client` for full set of client management
|
||||||
|
permissions. Alternatively you can choose `view-clients` for read-only or `create-client` for ability to create new clients.
|
||||||
|
These permissions grant user the capability to perform operations without the use of `Initial Access Token` or
|
||||||
|
`Registration Access Token`.
|
||||||
|
|
||||||
|
It's possible to not assign users any of `realm-management` roles. In that case user can still login with `Registration Client CLI`
|
||||||
|
but will not be able to use it without having possession of an `Initial Access Token`. Trying to perform any operations
|
||||||
|
without it will result in `403 Forbidden` error.
|
||||||
|
|
||||||
|
Administrator can issue `Initial Access Tokens` from `Admin Console` by selecting `Initial Access Token` tab under `Realm Settings`.
|
||||||
|
|
||||||
|
[[_configuring_a_client_for_use_with_client_registration_cli]]
|
||||||
|
=== Configuring a client for use with Client Registration CLI
|
||||||
|
|
||||||
|
By default the `Client Registration CLI` identifies as `admin-cli` client which is automatically configured for every new realm.
|
||||||
|
No additional client configuration is required when using login with a username. You may wish to strengthen security by
|
||||||
|
configuring the client `Access Type` as `Confidential`, and under `Credentials` tab select `ClientId and Secret`. When
|
||||||
|
running `kcreg config credentials` you would then also have to provide a secret e.g. by using `--secret`.
|
||||||
|
|
||||||
|
If you want to use a separate client configuration for `Registration Client CLI` then you can create a new client - for
|
||||||
|
example you can call it `reg-cli`. When running `kcreg config credentials` you then need to specify a `clientId` to use e.g. `--client reg-cli`.
|
||||||
|
|
||||||
|
If you want to use a service account associated with the client, then you need to enable a service account. In `Admin Client`
|
||||||
|
you go to `Clients` section, and select a client for edit. Then under `Settings` first change `Access Type` to `Confidential`.
|
||||||
|
Then toggle `Service Accounts Enabled` setting to `On`, and `Save` the configuration.
|
||||||
|
|
||||||
|
Under `Credentials` tab you can choose to configure either `Client Id and Secret`, or `Signed JWT`.
|
||||||
|
|
||||||
|
You can now avoid specifying user when using `kcreg config credentials` and only provide a client secret or keystore info.
|
||||||
|
|
||||||
|
[[_installing_client_registration_cli]]
|
||||||
|
=== Installing Client Registration CLI
|
||||||
|
|
||||||
|
Client Registration CLI is packaged inside Keycloak Server distribution. You can find execution scripts inside `bin` directory.
|
||||||
|
|
||||||
|
The Linux script is called `kcreg.sh`, and the one for Windows is called `kcreg.bat`.
|
||||||
|
|
||||||
|
In order to setup the client to be used from any location on the filesystem you may want to add Keycloak server directory to your PATH.
|
||||||
|
|
||||||
|
On Linux:
|
||||||
|
[source,bash]
|
||||||
|
----
|
||||||
|
$ export PATH=$PATH:$KEYCLOAK_HOME/bin
|
||||||
|
$ kcreg.sh
|
||||||
|
----
|
||||||
|
|
||||||
|
On Windows:
|
||||||
|
[source,bash]
|
||||||
|
----
|
||||||
|
c:\> set PATH=%PATH%;%KEYCLOAK_HOME%\bin
|
||||||
|
c:\> kcreg
|
||||||
|
----
|
||||||
|
|
||||||
|
[[_using_client_registration_cli]]
|
||||||
|
=== Using Client Registration CLI
|
||||||
|
|
||||||
|
Usually a user will first start an authenticated session by providing credentials, then perform some CRUD operations.
|
||||||
|
|
||||||
|
For example on Linux:
|
||||||
|
|
||||||
|
[source,bash]
|
||||||
|
----
|
||||||
|
$ kcreg.sh config credentials --server http://localhost:8080/auth --realm demo --user user --client reg-cli
|
||||||
|
$ kcreg.sh create -s clientId=my_client -s 'redirectUris=["http://localhost:8980/myapp/*"]'
|
||||||
|
$ kcreg.sh get my_client
|
||||||
|
----
|
||||||
|
|
||||||
|
Or on Windows:
|
||||||
|
|
||||||
|
[source,bash]
|
||||||
|
----
|
||||||
|
c:\> kcreg config credentials --server http://localhost:8080/auth --realm demo --user user --client reg-cli
|
||||||
|
c:\> kcreg create -s clientId=my_client -s "redirectUris=[\"http://localhost:8980/myapp/*\"]"
|
||||||
|
c:\> kcreg get my_client
|
||||||
|
----
|
||||||
|
|
||||||
|
|
||||||
|
In a production environment Keycloak has to be accessed with `https:` to avoid exposing tokens to network sniffers. If server's
|
||||||
|
certificate is not issued by one of the trusted CAs that are included in Java's default certificate truststore, then you will
|
||||||
|
need to prepare a truststore.jks file, and instruct `Client Registration CLI` to use it.
|
||||||
|
|
||||||
|
For example on Linux:
|
||||||
|
[source,bash]
|
||||||
|
----
|
||||||
|
$ kcreg.sh config truststore --trustpass $PASSWORD ~/.keycloak/truststore.jks
|
||||||
|
----
|
||||||
|
|
||||||
|
Or on Windows:
|
||||||
|
|
||||||
|
[source,bash]
|
||||||
|
----
|
||||||
|
c:\> kcreg config truststore --trustpass %PASSWORD% %HOMEPATH%\.keycloak\truststore.jks
|
||||||
|
----
|
||||||
|
|
||||||
|
|
||||||
|
[[_logging_in]]
|
||||||
|
==== Logging In
|
||||||
|
|
||||||
|
When logging in with `Client Registration CLI` you specify a server endpoint url, and a realm. Then you specify a username,
|
||||||
|
or alternatively you can only specify a client id, which will result in special service account being used. In the first case,
|
||||||
|
a password for the specified user has to be used at login. In the latter case there is no user password - only client secret
|
||||||
|
or a `Signed JWT` is used.
|
||||||
|
|
||||||
|
Regardless of the method, the account that logs in needs to have proper permissions in order to be able to perform client
|
||||||
|
registration operations. Keep in mind that any account can only have permissions to manage clients within the same realm.
|
||||||
|
If you need to manage different realms, you need to configure users in different realms with permissions to manage clients.
|
||||||
|
|
||||||
|
`Client Registration CLI` by itself does not support configuring the users, for that you would need to use `Admin Console`
|
||||||
|
web interface or `Admin Client CLI` once it's available.
|
||||||
|
|
||||||
|
When `kcreg` successfully logs in it receives authorization tokens and saves them into a private config file so they can be
|
||||||
|
used for subsequent invocations. See <<fake/#_working_with_alternative_configurations, next chapter>> for more info on configuration file.
|
||||||
|
|
||||||
|
See built-in help for more information on using `Client Registration CLI`.
|
||||||
|
|
||||||
|
|
||||||
|
For example on Linux:
|
||||||
|
[source,bash]
|
||||||
|
----
|
||||||
|
$ kcreg.sh help
|
||||||
|
----
|
||||||
|
|
||||||
|
|
||||||
|
Or on Windows:
|
||||||
|
[source,bash]
|
||||||
|
----
|
||||||
|
c:\> kcreg help
|
||||||
|
----
|
||||||
|
|
||||||
|
See `kcreg config credentials --help` for more information about starting an authenticated session.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[[_working_with_alternative_configurations]]
|
||||||
|
==== Working with alternative configurations
|
||||||
|
|
||||||
|
By default, `Client Registration CLI` automatically maintains a configuration file at a default location - `.keycloak/kcreg.config`
|
||||||
|
under user's home directory.
|
||||||
|
|
||||||
|
You can use `--config` option at any time to point to a different file / location. This way you can mantain multiple authenticated
|
||||||
|
sessions in parallel. It is safest to perform operations tied to a single config file from a single thread.
|
||||||
|
|
||||||
|
Make sure to not make a config file visible to other users on the system as it contains access tokens, and secrets that should be kept private.
|
||||||
|
|
||||||
|
You may want to avoid storing any secrets at all inside a config file for the price of less convenience and having to do more token requests.
|
||||||
|
In that case you can use `--no-config` option with all your commands. You will have to specify all authentication info with each
|
||||||
|
`kcreg` invocation.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[[_initial_access_and_registration_access_tokens]]
|
||||||
|
==== Initial Access and Registration Access Tokens
|
||||||
|
|
||||||
|
`Client Registration CLI` can be used by developers who don't have an account configured at Keycloak server they want to use.
|
||||||
|
That's possible when realm administrator issues developer an `Initial Access Token`. It is up to realm administrator to decide
|
||||||
|
how to issue and distribute these tokens. Admin can limit an Initial Access Token by maximum age, and a total number of clients
|
||||||
|
that can be created with it. Many Initial Access Tokens can be created, and it's up to realm administrator to distribute them.
|
||||||
|
|
||||||
|
Once a developer is in possession of Initial Access Token they can use it to create new clients without authenticating
|
||||||
|
with `kcreg config credentials`. Rather, Initial Access Token can be stored in configuration, or specified as part of `kcreg create`
|
||||||
|
command.
|
||||||
|
|
||||||
|
For example on Linux:
|
||||||
|
[source,bash]
|
||||||
|
----
|
||||||
|
$ kcreg.sh config initial-token $TOKEN
|
||||||
|
$ kcreg.sh create -s clientId=myclient
|
||||||
|
----
|
||||||
|
|
||||||
|
or
|
||||||
|
|
||||||
|
[source,bash]
|
||||||
|
----
|
||||||
|
$ kcreg.sh create -s clientId=myclient -t $TOKEN
|
||||||
|
----
|
||||||
|
|
||||||
|
|
||||||
|
On Windows:
|
||||||
|
[source,bash]
|
||||||
|
----
|
||||||
|
c:\> kcreg config initial-token %TOKEN%
|
||||||
|
c:\> kcreg create -s clientId=myclient
|
||||||
|
----
|
||||||
|
|
||||||
|
or
|
||||||
|
|
||||||
|
[source,bash]
|
||||||
|
----
|
||||||
|
c:\> kcreg create -s clientId=myclient -t %TOKEN%
|
||||||
|
----
|
||||||
|
|
||||||
|
|
||||||
|
When Initial Access Token is used, the server response will include a newly issued Registration Access Token for client that was
|
||||||
|
just created. Any subsequent operation for that client needs to be performed by authenticating with that token.
|
||||||
|
|
||||||
|
`Client Registration CLI` automatically uses its private configuration file to save, and make use of this token for each
|
||||||
|
created client. As long as the same configuration file is used for all client operations, the developer will not need to
|
||||||
|
authenticate in order to read, update, or delete a client they created.
|
||||||
|
|
||||||
|
|
||||||
|
You can read more about Initial Access and Registration Access Tokens in <<fake/../client-registration.adoc#_client_registration,Client Registration chapter>>.
|
||||||
|
|
||||||
|
See `kcreg config initial-token --help` and `kcreg config registration-token --help` for more information on how to configure them with `Client Registration CLI`.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[[_performing_crud_operations]]
|
||||||
|
==== Performing CRUD operations
|
||||||
|
|
||||||
|
|
||||||
|
After authenticating with credentials or configuring Initial Access Token, the first operation will usually be to create a new client.
|
||||||
|
|
||||||
|
We've seen the simplest command to create a new client already. Often we may want to use a prepared JSON file as a template,
|
||||||
|
and set / override some of the attributes. For example, this is how you read a JSON file in default client configuration format,
|
||||||
|
override any clientId it may contain with a new one, override / set any other attributes as well, and after successful creation
|
||||||
|
print the new client configuration to standard output.
|
||||||
|
|
||||||
|
On Linux:
|
||||||
|
[source,bash]
|
||||||
|
----
|
||||||
|
$ kcreg.sh create -s clientId=myclient -f client-template.json -s baseUrl=/myclient -s 'redirectUris=["/myclient/*"]' -o
|
||||||
|
----
|
||||||
|
|
||||||
|
On Windows:
|
||||||
|
[source,bash]
|
||||||
|
----
|
||||||
|
C:\> kcreg create -s clientId=myclient -f client-template.json -s baseUrl=/myclient -s "redirectUris=[\"/myclient/*\"]" -o
|
||||||
|
----
|
||||||
|
|
||||||
|
|
||||||
|
See `kcreg create --help` for more information about `kcreg create`.
|
||||||
|
|
||||||
|
|
||||||
|
You can use `kcreg attrs` to list the available attributes. Note, that many configuration attributes are not checked for
|
||||||
|
validity or consistency. It is up to you to specify proper values. Also note, that you should not have any `id` fields in your
|
||||||
|
template or specify them as arguments to `kcreg create`.
|
||||||
|
|
||||||
|
|
||||||
|
Once a new client is created you can retrieve it again by using `kcreg get`.
|
||||||
|
|
||||||
|
On Linux:
|
||||||
|
[source,bash]
|
||||||
|
----
|
||||||
|
$ kcreg.sh get myclient
|
||||||
|
----
|
||||||
|
|
||||||
|
On Windows:
|
||||||
|
[source,bash]
|
||||||
|
----
|
||||||
|
C:\> kcreg get myclient
|
||||||
|
----
|
||||||
|
|
||||||
|
|
||||||
|
You can also get an adapter configuration which you can drop into your web application in order to integrate with Keycloak server.
|
||||||
|
|
||||||
|
On Linux:
|
||||||
|
[source,bash]
|
||||||
|
----
|
||||||
|
$ kcreg.sh get myclient -e install
|
||||||
|
----
|
||||||
|
|
||||||
|
On Windows:
|
||||||
|
[source,bash]
|
||||||
|
----
|
||||||
|
C:\> kcreg get myclient -e install
|
||||||
|
----
|
||||||
|
|
||||||
|
See `kcreg get --help` for more information about `kcreg get`.
|
||||||
|
|
||||||
|
|
||||||
|
It's simple to update client configurations as well. There are two modes of updating.
|
||||||
|
|
||||||
|
One is to submit a complete new state to the server after getting current configuration, saving it into a file, editing it, and posting it back.
|
||||||
|
|
||||||
|
On Linux:
|
||||||
|
[source,bash]
|
||||||
|
----
|
||||||
|
$ kcreg.sh get myclient > myclient.json
|
||||||
|
$ vi myclient.json
|
||||||
|
$ kcreg.sh update myclient -f myclient.json
|
||||||
|
----
|
||||||
|
|
||||||
|
On Windows:
|
||||||
|
[source,bash]
|
||||||
|
----
|
||||||
|
C:\> kcreg get myclient > myclient.json
|
||||||
|
C:\> notepad myclient.json
|
||||||
|
C:\> kcreg update myclient -f myclient.json
|
||||||
|
----
|
||||||
|
|
||||||
|
|
||||||
|
Another is to get current client, set or delete fields on it, and post it back all in one single step.
|
||||||
|
|
||||||
|
On Linux:
|
||||||
|
[source,bash]
|
||||||
|
----
|
||||||
|
$ kcreg.sh update myclient -s enabled=false -d redirectUris
|
||||||
|
----
|
||||||
|
|
||||||
|
On Windows:
|
||||||
|
[source,bash]
|
||||||
|
----
|
||||||
|
C:\> kcreg update myclient -s enabled=false -d redirectUris
|
||||||
|
----
|
||||||
|
|
||||||
|
|
||||||
|
You can even use a file that contains only changes to be applied so you don't have to specify too many values as arguments.
|
||||||
|
In this case we specify `--merge` to tell `Client Registration CLI` that rather than treating mychanges.json as full
|
||||||
|
new configuration, it should see it as a set of attributes to be applied over existing configuration.
|
||||||
|
|
||||||
|
|
||||||
|
On Linux:
|
||||||
|
[source,bash]
|
||||||
|
----
|
||||||
|
$ kcreg.sh update myclient --merge -d redirectUris -f mychanges.json
|
||||||
|
----
|
||||||
|
|
||||||
|
On Windows:
|
||||||
|
[source,bash]
|
||||||
|
----
|
||||||
|
C:\> kcreg update myclient --merge -d redirectUris -f mychanges.json
|
||||||
|
----
|
||||||
|
|
||||||
|
See `kcreg update --help` for more information about `kcreg update`.
|
||||||
|
|
||||||
|
|
||||||
|
You may sometimes also need to delete a client.
|
||||||
|
|
||||||
|
On Linux:
|
||||||
|
[source,bash]
|
||||||
|
----
|
||||||
|
$ kcreg.sh delete myclient
|
||||||
|
----
|
||||||
|
|
||||||
|
On Windows:
|
||||||
|
[source,bash]
|
||||||
|
----
|
||||||
|
C:\> kcreg delete myclient
|
||||||
|
----
|
||||||
|
|
||||||
|
See `kcreg delete --help` for more information about `kcreg delete`.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[[_refreshing_invalid_registration_access_tokens]]
|
||||||
|
==== Refreshing Invalid Registration Access Tokens
|
||||||
|
|
||||||
|
When performing CRUD operation using `no-config` mode `Client Registration CLI` can no longer handle Registration Access Tokens for you.
|
||||||
|
In that case it is possible to lose track of most recently issued Registration Access Token for a client, which makes it impossible to
|
||||||
|
perform any further CRUD operations on that client without using credentials of an account with 'manage-clients' permissions.
|
||||||
|
|
||||||
|
If you have permissions you can reissue a new Registration Access Token for the client, and have it printed to stdout or saved to a config
|
||||||
|
file of your choice. If not you have to ask realm administrator to reissue a new Registration Access Token for your client, and send it
|
||||||
|
to you. You can then use the token by passing it to any CRUD command via `--token` option. You can also use `kcreg config registration-token`
|
||||||
|
command to save the new token in configuration file, and have `Client Registration CLI` automatically handle it for you from that point on.
|
||||||
|
|
||||||
|
See `kcreg update-token --help` for more information about `kcreg update-token`.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[[_troubleshooting_2]]
|
||||||
|
=== Troubleshooting
|
||||||
|
|
||||||
|
* Q: When logging in I get an error: `Parameter client_assertion_type is missing [invalid_client]`
|
||||||
|
+
|
||||||
|
A: Your client is configured with `Signed JWT` token credentials which means you have to use `--keystore` parameter when logging in.
|
20
securing_apps/topics/oidc/java/adapter-context.adoc
Normal file
20
securing_apps/topics/oidc/java/adapter-context.adoc
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
==== Security Context
|
||||||
|
|
||||||
|
The `KeycloakSecurityContext` interface is available if you need to access to the tokens directly. This could be useful if you want to retrieve additional
|
||||||
|
details from the token (such as user profile information) or you want to invoke a RESTful service that is protected by {{book.project.name}}.
|
||||||
|
|
||||||
|
In servlet environments it is available in secured invocations as an attribute in HttpServletRequest:
|
||||||
|
[source,java]
|
||||||
|
----
|
||||||
|
httpServletRequest
|
||||||
|
.getAttribute(KeycloakSecurityContext.class.getName());
|
||||||
|
----
|
||||||
|
|
||||||
|
Or, it is available in secure and insecure requests in the HttpSession:
|
||||||
|
|
||||||
|
[source,java]
|
||||||
|
----
|
||||||
|
httpServletRequest.getSession()
|
||||||
|
.getAttribute(KeycloakSecurityContext.class.getName());
|
||||||
|
----
|
||||||
|
|
35
securing_apps/topics/oidc/java/adapter_error_handling.adoc
Normal file
35
securing_apps/topics/oidc/java/adapter_error_handling.adoc
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
|
||||||
|
[[_adapter_error_handling]]
|
||||||
|
==== Error Handling
|
||||||
|
|
||||||
|
{{book.project.name}} has some error handling facilities for servlet based client adapters.
|
||||||
|
When an error is encountered in authentication, {{book.project.name}} will call `HttpServletResponse.sendError()`.
|
||||||
|
You can set up an error-page within your `web.xml` file to handle the error however you want.
|
||||||
|
{{book.project.name}} may throw 400, 401, 403, and 500 errors.
|
||||||
|
|
||||||
|
[source,xml]
|
||||||
|
----
|
||||||
|
<error-page>
|
||||||
|
<error-code>404</error-code>
|
||||||
|
<location>/ErrorHandler</location>
|
||||||
|
</error-page>
|
||||||
|
----
|
||||||
|
|
||||||
|
{{book.project.name}} also sets a `HttpServletRequest` attribute that you can retrieve.
|
||||||
|
The attribute name is `org.keycloak.adapters.spi.AuthenticationError`, which should be casted to `org.keycloak.adapters.OIDCAuthenticationError`.
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
[source,java]
|
||||||
|
----
|
||||||
|
import org.keycloak.adapters.OIDCAuthenticationError;
|
||||||
|
import org.keycloak.adapters.OIDCAuthenticationError.Reason;
|
||||||
|
...
|
||||||
|
|
||||||
|
OIDCAuthenticationError error = (OIDCAuthenticationError) httpServletRequest
|
||||||
|
.getAttribute('org.keycloak.adapters.spi.AuthenticationError');
|
||||||
|
|
||||||
|
Reason reason = error.getReason();
|
||||||
|
System.out.println(reason.name());
|
||||||
|
----
|
||||||
|
|
115
securing_apps/topics/oidc/java/application-clustering.adoc
Normal file
115
securing_apps/topics/oidc/java/application-clustering.adoc
Normal file
|
@ -0,0 +1,115 @@
|
||||||
|
[[_applicationclustering]]
|
||||||
|
==== Application Clustering
|
||||||
|
|
||||||
|
{% if book.community %}
|
||||||
|
This chapter is related to supporting clustered applications deployed to JBoss EAP, WildFly and JBoss AS.
|
||||||
|
{% endif %}
|
||||||
|
{% if book.product %}
|
||||||
|
This chapter is related to supporting clustered applications deployed to JBoss EAP.
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
There are a few options available depending on whether your application is:
|
||||||
|
|
||||||
|
* Stateless or stateful
|
||||||
|
* Distributable (replicated http session) or non-distributable
|
||||||
|
* Relying on sticky sessions provided by load balancer
|
||||||
|
* Hosted on same domain as {{book.project.name}}
|
||||||
|
|
||||||
|
Dealing with clustering is not quite as simple as for a regular application. Mainly due to the fact that both the browser and the server-side application
|
||||||
|
sends requests to {{book.project.name}}, so it's not as simple as enabling sticky sessions on your load balancer.
|
||||||
|
|
||||||
|
===== Stateless token store
|
||||||
|
|
||||||
|
By default, the web application secured by {{book.project.name}} uses the HTTP session to store security context. This means that you either have to
|
||||||
|
enable sticky sessions or replicate the HTTP session.
|
||||||
|
|
||||||
|
As an alternative to storing the security context in the HTTP session the adapter can be configured to store this in a cookie instead. This is useful if you want
|
||||||
|
to make your application stateless or if you don't want to store the security context in the HTTP session.
|
||||||
|
|
||||||
|
To use the cookie store for saving the security context, edit your applications `WEB-INF/keycloak.json` and add:
|
||||||
|
[source,json]
|
||||||
|
----
|
||||||
|
"token-store": "cookie"
|
||||||
|
----
|
||||||
|
|
||||||
|
NOTE: The default value for `token-store` is `session`, which stores the security context in the HTTP session.
|
||||||
|
|
||||||
|
One limitation of using the cookie store is that the whole security context is passed in the cookie for every HTTP request. This may impact performance.
|
||||||
|
|
||||||
|
Another small limitation is limited support for Single-Sign Out. It works without issues if you init servlet logout (HttpServletRequest.logout) from the
|
||||||
|
application itself as the adapter will delete the KEYCLOAK_ADAPTER_STATE cookie. However, back-channel logout initialized from a different application isn't
|
||||||
|
propagated by {{book.project.name}} to applications using cookie store. Hence it's recommended to use a short value for the access token timeout (for example 1 minute).
|
||||||
|
|
||||||
|
===== Relative URI optimization
|
||||||
|
|
||||||
|
In deployment scenarios where {{book.project.name}} and the application is hosted on the same domain (through a reverse proxy or load balancer) it can be
|
||||||
|
convenient to use relative URI options in your client configuration.
|
||||||
|
|
||||||
|
With relative URIs the URI is resolved as relative to the URL of the URL used to access {{book.project.name}}.
|
||||||
|
|
||||||
|
For example if the URL to your application is `$$https://acme.org/myapp$$` and the URL to {{book.project.name}} is `$$https://acme.org/auth$$`, then you can use
|
||||||
|
the redirect-uri `/myapp` instead of `$$https://acme.org/myapp$$`.
|
||||||
|
|
||||||
|
===== Admin URL configuration
|
||||||
|
|
||||||
|
Admin URL for a particular client can be configured in the {{book.project.name}} Administration Console.
|
||||||
|
It's used by the {{book.project.name}} server to send backend requests to the application for various tasks, like logout users or push revocation policies.
|
||||||
|
|
||||||
|
For example the way backchannel logout works is:
|
||||||
|
|
||||||
|
. User sends logout request from one application
|
||||||
|
. The application sends logout request to {{book.project.name}}
|
||||||
|
. The {{book.project.name}} server invalidates the user session
|
||||||
|
. The {{book.project.name}} server then sends a backchannel request to application with an admin url that are associated with the session
|
||||||
|
. When an application receives the logout request it invalidates the corresponding HTTP session
|
||||||
|
|
||||||
|
If admin URL contains `${application.session.host}` it will be replaced with the URL to the node associated with the HTTP session.
|
||||||
|
|
||||||
|
[[_registration_app_nodes]]
|
||||||
|
===== Registration of application nodes
|
||||||
|
|
||||||
|
The previous section describes how {{book.project.name}} can send logout request to node associated with a specific HTTP session.
|
||||||
|
However, in some cases admin may want to propagate admin tasks to all registered cluster nodes, not just one of them.
|
||||||
|
For example to push a new not before policy to the application or to logout all users from the application.
|
||||||
|
|
||||||
|
In this case {{book.project.name}} needs to be aware of all application cluster nodes, so it can send the event to all of them.
|
||||||
|
To achieve this, we support auto-discovery mechanism:
|
||||||
|
|
||||||
|
. When a new application node joins the cluster, it sends a registration request to the {{book.project.name}} server
|
||||||
|
. The request may be re-sent to {{book.project.name}} in configured periodic intervals
|
||||||
|
. If the {{book.project.name}} server doesn't receive a re-registration request within a specified timeout then it automatically unregisters the specific node
|
||||||
|
. The node is also unregistered in {{book.project.name}} when it sends an unregistration request, which is usually during node shutdown or application undeployment.
|
||||||
|
This may not work properly for forced shutdown when undeployment listeners are not invoked, which results in the need for automatic unregistration
|
||||||
|
|
||||||
|
Sending startup registrations and periodic re-registration is disabled by default as it's only required for some clustered applications.
|
||||||
|
|
||||||
|
To enable the feature edit the `WEB-INF/keycloak.json` file for your application and add:
|
||||||
|
|
||||||
|
[source]
|
||||||
|
----
|
||||||
|
"register-node-at-startup": true,
|
||||||
|
"register-node-period": 600,
|
||||||
|
----
|
||||||
|
|
||||||
|
This means the adapter will send the registration request on startup and re-register every 10 minutes.
|
||||||
|
|
||||||
|
In the {{book.project.name}} Administration Console you can specify the maximum node re-registration timeout (should be larger than _register-node-period_ from
|
||||||
|
the adapter configuration). You can also manually add and remove cluster nodes in through the Adminstration Console, which is useful if you don't want to rely
|
||||||
|
on the automatic registration feature or if you want to remove stale application nodes in the event your not using the automatic unregistration feature.
|
||||||
|
|
||||||
|
[[_refresh_token_each_req]]
|
||||||
|
===== Refresh token in each request
|
||||||
|
|
||||||
|
By default the application adapter will only refresh the access token when it's expired. However, you can also configure the adapter to refresh the token on every
|
||||||
|
request. This may have a performance impact as your application will send more requests to the {{book.project.name}} server.
|
||||||
|
|
||||||
|
To enable the feature edit the `WEB-INF/keycloak.json` file for your application and add:
|
||||||
|
|
||||||
|
[source]
|
||||||
|
----
|
||||||
|
"always-refresh-token": true
|
||||||
|
----
|
||||||
|
|
||||||
|
NOTE: This may have a significant impact on performance. Only enable this feature if you can't rely on backchannel messages to propagate logout and not before
|
||||||
|
policies. Another thing to consider is that by default access tokens has a short expiration so even if logout is not propagated the token will expire within
|
||||||
|
minutes of the logout.
|
74
securing_apps/topics/oidc/java/client-authentication.adoc
Normal file
74
securing_apps/topics/oidc/java/client-authentication.adoc
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
[[_client_authentication_adapter]]
|
||||||
|
==== Client Authentication
|
||||||
|
|
||||||
|
When confidential OIDC client needs to send backchannel request (eg. exchange code for the token, or refresh token) it needs to authenticate
|
||||||
|
against {{book.project.name}} server. By default, there are 2 possibilities how to authenticate client:
|
||||||
|
|
||||||
|
===== Client ID and Client Secret
|
||||||
|
|
||||||
|
This is traditional method described in OAuth2 specification. Client has a secret, which needs to be known to both adapter (application) and {{book.project.name}} server.
|
||||||
|
You can simply generate the secret for particular client in {{book.project.name}} admin console and then paste this secret in the `keycloak.json` file on the application's side:
|
||||||
|
|
||||||
|
|
||||||
|
[source]
|
||||||
|
----
|
||||||
|
"credentials": {
|
||||||
|
"secret": "19666a4f-32dd-4049-b082-684c74115f28"
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
|
===== Client authentication with signed JWT
|
||||||
|
|
||||||
|
This is based on the https://tools.ietf.org/html/rfc7523[RFC7523] specification. It works this way:
|
||||||
|
|
||||||
|
* Client must have the private key and certificate. In case of {{book.project.name}} this is available through the traditional `keystore` file, which is either available
|
||||||
|
on client application's classpath or somewhere on the filesystem.
|
||||||
|
|
||||||
|
* Once the client application is started, it allows to download it's public key in https://self-issued.info/docs/draft-ietf-jose-json-web-key.html[JWKS] format on URL
|
||||||
|
like http://myhost.com/myapp/k_jwks assuming that http://myhost.com/myapp is the base URL of your client application. This URL can be used by the {{book.project.name}} (see below).
|
||||||
|
|
||||||
|
* During authentication, client generates JWT token and signs it with his private key and sends it to the {{book.project.name}} in
|
||||||
|
the particular backchannel request (eg. code-to-token request) in the `client_assertion` parameter.
|
||||||
|
|
||||||
|
* {{book.project.name}} must have public key or certificate of the client, so that it can verify the signature on JWT. In {{book.project.name}} you either
|
||||||
|
need to configure client credentials for your client. First you need to choose `Signed JWT` as the method of authenticating your client in the tab `Credentials` in admin console.
|
||||||
|
Then you can choose either to:
|
||||||
|
** Configure the JWKS URL where {{book.project.name}} can download the client's public keys. This can be URL like http://myhost.com/myapp/k_jwks (see details above). This is flexible as
|
||||||
|
client can rotate it's keys anytime and {{book.project.name}} will then always download new keys when needed without need to change something in it's configuration. More acurately, {{book.project.name}}
|
||||||
|
will download new keys when it sees the token signed by unknown `kid` (Key ID).
|
||||||
|
** Upload the client's public key or certificate - either in PEM format, in JWK format or from keystore. With this option, public key is hardcoded and
|
||||||
|
needs to be changed anytime when client generates new keypair.
|
||||||
|
You can even generate your own keystore from {{book.project.name}} admin console if you don't have your own ready.
|
||||||
|
See {{book.project.doc_base_url}}{{book.project.doc_info_version_url}}{{book.adminguide.link}}[{{book.adminguide.name}}] for more details of setup in {{book.project.name}} admin console.
|
||||||
|
|
||||||
|
For setup on adapter's side you need to have something like this in your `keycloak.json` file:
|
||||||
|
|
||||||
|
[source]
|
||||||
|
----
|
||||||
|
"credentials": {
|
||||||
|
"jwt": {
|
||||||
|
"client-keystore-file": "classpath:keystore-client.jks",
|
||||||
|
"client-keystore-type": "JKS",
|
||||||
|
"client-keystore-password": "storepass",
|
||||||
|
"client-key-password": "keypass",
|
||||||
|
"client-key-alias": "clientkey",
|
||||||
|
"token-expiration": 10
|
||||||
|
}
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
|
With this configuration, the keystore file `keystore-client.jks` must be available on classpath in your WAR. If you don't use prefix `classpath:`
|
||||||
|
you can point to any file on the filesystem where client application is running.
|
||||||
|
|
||||||
|
{% if book.community %}
|
||||||
|
For inspiration, you can take a look at the examples distribution into the main demo example into the `product-portal` application.
|
||||||
|
|
||||||
|
|
||||||
|
===== Add your own client authentication method
|
||||||
|
|
||||||
|
This is possible. You will need to implement both client's side and server's side providers. See `Authentication SPI` section
|
||||||
|
in {{book.developerguide.link}}[{{book.developerguide.name}}] for more details.
|
||||||
|
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
|
38
securing_apps/topics/oidc/java/fuse-adapter.adoc
Executable file
38
securing_apps/topics/oidc/java/fuse-adapter.adoc
Executable file
|
@ -0,0 +1,38 @@
|
||||||
|
|
||||||
|
[[_fuse_adapter]]
|
||||||
|
==== JBoss Fuse Adapter
|
||||||
|
|
||||||
|
{% if book.product %}
|
||||||
|
NOTE: JBoss Fuse is a Technology Preview feature and is not fully supported.
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
Currently {{book.project.name}} supports securing your web applications running inside http://developers.redhat.com/products/fuse/overview/[JBoss Fuse].
|
||||||
|
|
||||||
|
{% if book.community %}
|
||||||
|
It leverages <<fake/../jetty9-adapter.adoc#_jetty9_adapter,Jetty 9 adapter>> as {{book.fuseVersion}} is bundled with http://eclipse.org/jetty/[Jetty 9.2 server]
|
||||||
|
under the covers and Jetty is used for running various kinds of web applications.
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
WARNING: The only supported Fuse version is {{book.fuseVersion}}. If you use earlier versions of Fuse, it's possible that some functionalities won't work correctly.
|
||||||
|
Especially the http://hawt.io[Hawtio] integration won't work with earlier versions of Fuse.
|
||||||
|
|
||||||
|
What is supported for Fuse is:
|
||||||
|
|
||||||
|
* Security for classic WAR applications deployed on Fuse with https://ops4j1.jira.com/wiki/display/ops4j/Pax+Web+Extender+-+War[Pax Web War Extender].
|
||||||
|
* Security for servlets deployed on Fuse as OSGI services with https://ops4j1.jira.com/wiki/display/ops4j/Pax+Web+Extender+-+Whiteboard[Pax Web Whiteboard Extender].
|
||||||
|
* Security for http://camel.apache.org/[Apache Camel] Jetty endpoints running with http://camel.apache.org/jetty.html[Camel Jetty] component.
|
||||||
|
* Security for http://cxf.apache.org/[Apache CXF] endpoints running on their own separate http://cxf.apache.org/docs/jetty-configuration.html[Jetty engine].
|
||||||
|
* Security for http://cxf.apache.org/[Apache CXF] endpoints running on default engine provided by CXF servlet.
|
||||||
|
* Security for SSH and JMX admin access.
|
||||||
|
* Security for http://hawt.io[Hawtio admin console]
|
||||||
|
|
||||||
|
===== How to secure your web applications inside Fuse
|
||||||
|
|
||||||
|
The first thing to do is usually installing of {{book.project.name}} Karaf feature. Then do the steps according to what type of application you want to secure.
|
||||||
|
Basically all mentioned web applications require to inject {{book.project.name}} Jetty authenticator into underlying Jetty server . The steps to achieve it are bit different
|
||||||
|
according to application type. The details are described in individual sub-chapters.
|
||||||
|
|
||||||
|
{% if book.community %}
|
||||||
|
The best place to start is look at Fuse demo bundled as part of {{book.project.name}} examples in directory `fuse` . Most of the steps should be understandable from testing and
|
||||||
|
understanding the demo.
|
||||||
|
{% endif %}
|
155
securing_apps/topics/oidc/java/fuse/camel.adoc
Normal file
155
securing_apps/topics/oidc/java/fuse/camel.adoc
Normal file
|
@ -0,0 +1,155 @@
|
||||||
|
[[_fuse_adapter_camel]]
|
||||||
|
|
||||||
|
===== Apache Camel Application
|
||||||
|
|
||||||
|
* You can secure your Apache camel endpoint using http://camel.apache.org/jetty.html[camel-jetty] component by adding securityHandler with `KeycloakJettyAuthenticator` and
|
||||||
|
proper security constraints injected. You can add file `OSGI-INF/blueprint/blueprint.xml` into your camel application with the configuration similar to below.
|
||||||
|
The roles, security constraint mappings and {{book.project.name}} adapter configuration may be a bit different according to your environment and needs:
|
||||||
|
|
||||||
|
[source,xml]
|
||||||
|
----
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xmlns:camel="http://camel.apache.org/schema/blueprint"
|
||||||
|
xsi:schemaLocation="
|
||||||
|
http://www.osgi.org/xmlns/blueprint/v1.0.0 http://www.osgi.org/xmlns/blueprint/v1.0.0/blueprint.xsd
|
||||||
|
http://camel.apache.org/schema/blueprint http://camel.apache.org/schema/blueprint/camel-blueprint.xsd">
|
||||||
|
|
||||||
|
<bean id="kcAdapterConfig" class="org.keycloak.representations.adapters.config.AdapterConfig">
|
||||||
|
<property name="realm" value="demo"/>
|
||||||
|
<property name="resource" value="admin-camel-endpoint"/>
|
||||||
|
<property name="bearerOnly" value="true"/>
|
||||||
|
<property name="authServerUrl" value="http://localhost:8080/auth" />
|
||||||
|
<property name="sslRequired" value="EXTERNAL"/>
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<bean id="keycloakAuthenticator" class="org.keycloak.adapters.jetty.KeycloakJettyAuthenticator">
|
||||||
|
<property name="adapterConfig" ref="kcAdapterConfig"/>
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<bean id="constraint" class="org.eclipse.jetty.util.security.Constraint">
|
||||||
|
<property name="name" value="Customers"/>
|
||||||
|
<property name="roles">
|
||||||
|
<list>
|
||||||
|
<value>admin</value>
|
||||||
|
</list>
|
||||||
|
</property>
|
||||||
|
<property name="authenticate" value="true"/>
|
||||||
|
<property name="dataConstraint" value="0"/>
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<bean id="constraintMapping" class="org.eclipse.jetty.security.ConstraintMapping">
|
||||||
|
<property name="constraint" ref="constraint"/>
|
||||||
|
<property name="pathSpec" value="/*"/>
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<bean id="securityHandler" class="org.eclipse.jetty.security.ConstraintSecurityHandler">
|
||||||
|
<property name="authenticator" ref="keycloakAuthenticator" />
|
||||||
|
<property name="constraintMappings">
|
||||||
|
<list>
|
||||||
|
<ref component-id="constraintMapping" />
|
||||||
|
</list>
|
||||||
|
</property>
|
||||||
|
<property name="authMethod" value="BASIC"/>
|
||||||
|
<property name="realmName" value="does-not-matter"/>
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<bean id="sessionHandler" class="org.keycloak.adapters.jetty.spi.WrappingSessionHandler">
|
||||||
|
<property name="handler" ref="securityHandler" />
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<bean id="helloProcessor" class="org.keycloak.example.CamelHelloProcessor" />
|
||||||
|
|
||||||
|
<camelContext id="blueprintContext"
|
||||||
|
trace="false"
|
||||||
|
xmlns="http://camel.apache.org/schema/blueprint">
|
||||||
|
<route id="httpBridge">
|
||||||
|
<from uri="jetty:http://0.0.0.0:8383/admin-camel-endpoint?handlers=sessionHandler&matchOnUriPrefix=true" />
|
||||||
|
<process ref="helloProcessor" />
|
||||||
|
<log message="The message from camel endpoint contains ${body}"/>
|
||||||
|
</route>
|
||||||
|
</camelContext>
|
||||||
|
|
||||||
|
</blueprint>
|
||||||
|
----
|
||||||
|
|
||||||
|
|
||||||
|
* The `Import-Package` in `META-INF/MANIFEST.MF` needs to contain those imports:
|
||||||
|
|
||||||
|
[source, subs="attributes"]
|
||||||
|
----
|
||||||
|
javax.servlet;version="[3,4)",
|
||||||
|
javax.servlet.http;version="[3,4)",
|
||||||
|
org.apache.camel.*,
|
||||||
|
org.apache.camel;version="[2.13,3)",
|
||||||
|
org.eclipse.jetty.security;version="[8,10)",
|
||||||
|
org.eclipse.jetty.server.nio;version="[8,10)",
|
||||||
|
org.eclipse.jetty.util.security;version="[8,10)",
|
||||||
|
org.keycloak.*;version="{{book.project.versionMvn}}",
|
||||||
|
org.osgi.service.blueprint,
|
||||||
|
org.osgi.service.blueprint.container,
|
||||||
|
org.osgi.service.event,
|
||||||
|
----
|
||||||
|
|
||||||
|
===== Camel RestDSL
|
||||||
|
|
||||||
|
Camel RestDSL is a Camel feature to define your REST endpoints in a fluent way.
|
||||||
|
But under the hood, the capability to provide all this magic, is still demanded to specific implementation classes and
|
||||||
|
you have to instruct them on how to integrate with {{book.project.name}}.
|
||||||
|
|
||||||
|
The way to configure the integration mechanism depends on the Camel component that you configure your RestDSL defined routes to work with.
|
||||||
|
|
||||||
|
This is an example that show how to do it while using Jetty engine, with reference to some beans defined in the example above.
|
||||||
|
|
||||||
|
[source,xml]
|
||||||
|
----
|
||||||
|
<bean id="securityHandlerRest" class="org.eclipse.jetty.security.ConstraintSecurityHandler">
|
||||||
|
<property name="authenticator" ref="keycloakAuthenticator" />
|
||||||
|
<property name="constraintMappings">
|
||||||
|
<list>
|
||||||
|
<ref component-id="constraintMapping" />
|
||||||
|
</list>
|
||||||
|
</property>
|
||||||
|
<property name="authMethod" value="BASIC"/>
|
||||||
|
<property name="realmName" value="does-not-matter"/>
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<bean id="sessionHandlerRest" class="org.keycloak.adapters.jetty.spi.WrappingSessionHandler">
|
||||||
|
<property name="handler" ref="securityHandlerRest" />
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
|
||||||
|
<camelContext id="blueprintContext"
|
||||||
|
trace="false"
|
||||||
|
xmlns="http://camel.apache.org/schema/blueprint">
|
||||||
|
|
||||||
|
<restConfiguration component="jetty" contextPath="/restdsl"
|
||||||
|
port="8484">
|
||||||
|
<!--the link with Keycloak security handlers happens here-->
|
||||||
|
<endpointProperty key="handlers" value="sessionHandlerRest"></endpointProperty>
|
||||||
|
<endpointProperty key="matchOnUriPrefix" value="true"></endpointProperty>
|
||||||
|
</restConfiguration>
|
||||||
|
|
||||||
|
<rest path="/hello" >
|
||||||
|
<description>Hello rest service</description>
|
||||||
|
<get uri="/{id}" outType="java.lang.String">
|
||||||
|
<description>Just an helllo</description>
|
||||||
|
<to uri="direct:justDirect" />
|
||||||
|
</get>
|
||||||
|
|
||||||
|
</rest>
|
||||||
|
|
||||||
|
<route id="justDirect">
|
||||||
|
<from uri="direct:justDirect"/>
|
||||||
|
<process ref="helloProcessor" />
|
||||||
|
<log message="RestDSL correctly invoked ${body}"/>
|
||||||
|
<setBody>
|
||||||
|
<constant>(__This second sentence is returned from a Camel RestDSL endpoint__)</constant>
|
||||||
|
</setBody>
|
||||||
|
</route>
|
||||||
|
|
||||||
|
</camelContext>
|
||||||
|
|
||||||
|
----
|
102
securing_apps/topics/oidc/java/fuse/classic-war.adoc
Normal file
102
securing_apps/topics/oidc/java/fuse/classic-war.adoc
Normal file
|
@ -0,0 +1,102 @@
|
||||||
|
|
||||||
|
[[_fuse_adapter_classic_war]]
|
||||||
|
===== Secure Classic WAR application
|
||||||
|
|
||||||
|
The needed steps to secure your WAR are:
|
||||||
|
|
||||||
|
* Declare needed security constraints in `/WEB-INF/web.xml` . You also need to declare login-config and all the roles inside security-role.
|
||||||
|
The example configuration can look like this:
|
||||||
|
|
||||||
|
[source,xml]
|
||||||
|
----
|
||||||
|
<?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">
|
||||||
|
|
||||||
|
<module-name>customer-portal</module-name>
|
||||||
|
|
||||||
|
<welcome-file-list>
|
||||||
|
<welcome-file>index.html</welcome-file>
|
||||||
|
</welcome-file-list>
|
||||||
|
|
||||||
|
<security-constraint>
|
||||||
|
<web-resource-collection>
|
||||||
|
<web-resource-name>Customers</web-resource-name>
|
||||||
|
<url-pattern>/customers/*</url-pattern>
|
||||||
|
</web-resource-collection>
|
||||||
|
<auth-constraint>
|
||||||
|
<role-name>user</role-name>
|
||||||
|
</auth-constraint>
|
||||||
|
</security-constraint>
|
||||||
|
|
||||||
|
<login-config>
|
||||||
|
<auth-method>BASIC</auth-method>
|
||||||
|
<realm-name>does-not-matter</realm-name>
|
||||||
|
</login-config>
|
||||||
|
|
||||||
|
<security-role>
|
||||||
|
<role-name>admin</role-name>
|
||||||
|
</security-role>
|
||||||
|
<security-role>
|
||||||
|
<role-name>user</role-name>
|
||||||
|
</security-role>
|
||||||
|
</web-app>
|
||||||
|
----
|
||||||
|
|
||||||
|
* Add `jetty-web.xml` file with the authenticator to `/WEB-INF/jetty-web.xml` . Typically it will look like this:
|
||||||
|
|
||||||
|
[source,xml]
|
||||||
|
----
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN"
|
||||||
|
"http://www.eclipse.org/jetty/configure_9_0.dtd">
|
||||||
|
<Configure class="org.eclipse.jetty.webapp.WebAppContext">
|
||||||
|
<Get name="securityHandler">
|
||||||
|
<Set name="authenticator">
|
||||||
|
<New class="org.keycloak.adapters.jetty.KeycloakJettyAuthenticator">
|
||||||
|
</New>
|
||||||
|
</Set>
|
||||||
|
</Get>
|
||||||
|
</Configure>
|
||||||
|
----
|
||||||
|
|
||||||
|
* Add `/WEB-INF/keycloak.json` with your {{book.project.name}} configuration. The format of this config file is described
|
||||||
|
in the <<fake/../../java-adapter-config.adoc#_java_adapter_config,Java Adapters Config>> section. It is also possible to have this file available externally as described below.
|
||||||
|
|
||||||
|
* Make sure your WAR imports `org.keycloak.adapters.jetty` and maybe some more packages in `META-INF/MANIFEST.MF` file in header `Import-Package`. It's
|
||||||
|
recommended to use `maven-bundle-plugin` in your project to properly generate OSGI headers in manifest.
|
||||||
|
Note that "*" resolution for package doesn't import `org.keycloak.adapters.jetty` package
|
||||||
|
as it's not used by application or Blueprint or Spring descriptor, but it's used just in `jetty-web.xml` file. So list of the packages to import may look like this:
|
||||||
|
|
||||||
|
[source, subs="attributes"]
|
||||||
|
----
|
||||||
|
org.keycloak.adapters.jetty;version="{{book.project.versionMvn}}",
|
||||||
|
org.keycloak.adapters;version="{{book.project.versionMvn}}",
|
||||||
|
org.keycloak.constants;version="{{book.project.versionMvn}}",
|
||||||
|
org.keycloak.util;version="{{book.project.versionMvn}}",
|
||||||
|
org.keycloak.*;version="{{book.project.versionMvn}}",
|
||||||
|
*;resolution:=optional
|
||||||
|
----
|
||||||
|
|
||||||
|
====== External adapter configuration
|
||||||
|
|
||||||
|
This is for the case when you don't want adapter configuration file `keycloak.json` to be bundled inside your WAR application. Instead it will be available
|
||||||
|
externally and loaded based on naming conventions.
|
||||||
|
|
||||||
|
To enable the functionality you need to add this section to your `web.xml`:
|
||||||
|
|
||||||
|
[source,xml]
|
||||||
|
----
|
||||||
|
<context-param>
|
||||||
|
<param-name>keycloak.config.resolver</param-name>
|
||||||
|
<param-value>org.keycloak.adapters.osgi.PathBasedKeycloakConfigResolver</param-value>
|
||||||
|
</context-param>
|
||||||
|
----
|
||||||
|
|
||||||
|
That component will use `keycloak.config` or `karaf.etc` java properties to look for a base folder to look for the configuration.
|
||||||
|
Inside one of those folders it will look for a file called `<your_web_context>-keycloak.json`.
|
||||||
|
|
||||||
|
So for example if your web application has context `my-portal`, then your adapter configuration will be loaded from the file `$FUSE_HOME/etc/my-portal-keycloak.json` .
|
||||||
|
|
99
securing_apps/topics/oidc/java/fuse/cxf-builtin.adoc
Normal file
99
securing_apps/topics/oidc/java/fuse/cxf-builtin.adoc
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
|
||||||
|
[[_fuse_adapter_cxf_builtin]]
|
||||||
|
===== Secure Apache CXF Endpoint on default Jetty Engine
|
||||||
|
|
||||||
|
Some services automatically come with deployed servlets on startup. One of such services is CXF servlet running on
|
||||||
|
$$http://localhost:8181/cxf$$ context. Securing such endpoints is quite tricky. The approach, which {{book.project.name}} is currently using,
|
||||||
|
is providing ServletReregistrationService, which undeploys builtin servlet at startup, so you are able to re-deploy it again on context secured by {{book.project.name}}.
|
||||||
|
This is how configuration file `OSGI-INF/blueprint/blueprint.xml` inside your application may look like. Note it adds JAX-RS `customerservice` endpoint,
|
||||||
|
which is endpoint specific to your application, but more importantly, it secures whole `/cxf` context.
|
||||||
|
|
||||||
|
[source,xml]
|
||||||
|
----
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xmlns:jaxrs="http://cxf.apache.org/blueprint/jaxrs"
|
||||||
|
xsi:schemaLocation="
|
||||||
|
http://www.osgi.org/xmlns/blueprint/v1.0.0 http://www.osgi.org/xmlns/blueprint/v1.0.0/blueprint.xsd
|
||||||
|
http://cxf.apache.org/blueprint/jaxrs http://cxf.apache.org/schemas/blueprint/jaxrs.xsd">
|
||||||
|
|
||||||
|
<!-- JAXRS Application -->
|
||||||
|
|
||||||
|
<bean id="customerBean" class="org.keycloak.example.rs.CxfCustomerService" />
|
||||||
|
|
||||||
|
<jaxrs:server id="cxfJaxrsServer" address="/customerservice">
|
||||||
|
<jaxrs:providers>
|
||||||
|
<bean class="com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider" />
|
||||||
|
</jaxrs:providers>
|
||||||
|
<jaxrs:serviceBeans>
|
||||||
|
<ref component-id="customerBean" />
|
||||||
|
</jaxrs:serviceBeans>
|
||||||
|
</jaxrs:server>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- Securing of whole /cxf context by unregister default cxf servlet from paxweb and re-register with applied security constraints -->
|
||||||
|
|
||||||
|
<bean id="cxfConstraintMapping" class="org.eclipse.jetty.security.ConstraintMapping">
|
||||||
|
<property name="constraint">
|
||||||
|
<bean class="org.eclipse.jetty.util.security.Constraint">
|
||||||
|
<property name="name" value="cst1"/>
|
||||||
|
<property name="roles">
|
||||||
|
<list>
|
||||||
|
<value>user</value>
|
||||||
|
</list>
|
||||||
|
</property>
|
||||||
|
<property name="authenticate" value="true"/>
|
||||||
|
<property name="dataConstraint" value="0"/>
|
||||||
|
</bean>
|
||||||
|
</property>
|
||||||
|
<property name="pathSpec" value="/cxf/*"/>
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<bean id="cxfKeycloakPaxWebIntegration" class="org.keycloak.adapters.osgi.PaxWebIntegrationService"
|
||||||
|
init-method="start" destroy-method="stop">
|
||||||
|
<property name="bundleContext" ref="blueprintBundleContext" />
|
||||||
|
<property name="jettyWebXmlLocation" value="/WEB-INF/jetty-web.xml" />
|
||||||
|
<property name="constraintMappings">
|
||||||
|
<list>
|
||||||
|
<ref component-id="cxfConstraintMapping" />
|
||||||
|
</list>
|
||||||
|
</property>
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<bean id="defaultCxfReregistration" class="org.keycloak.adapters.osgi.ServletReregistrationService" depends-on="cxfKeycloakPaxWebIntegration"
|
||||||
|
init-method="start" destroy-method="stop">
|
||||||
|
<property name="bundleContext" ref="blueprintBundleContext" />
|
||||||
|
<property name="managedServiceReference">
|
||||||
|
<reference interface="org.osgi.service.cm.ManagedService" filter="(service.pid=org.apache.cxf.osgi)" timeout="5000" />
|
||||||
|
</property>
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
</blueprint>
|
||||||
|
----
|
||||||
|
|
||||||
|
As a side effect, all other CXF services running on default CXF HTTP destination will be secured too. Similarly when the application is undeployed, then
|
||||||
|
whole `/cxf` context will become unsecured too. For this reason, it's recommended to use your own Jetty engine for your apps like
|
||||||
|
described in <<fake/../cxf-separate.adoc#_fuse_adapter_cxf_separate,Secure CXF Application on separate Jetty Engine>> as then you have more
|
||||||
|
control over security for each application individually.
|
||||||
|
|
||||||
|
* You may need to have directory `WEB-INF` inside your project (even if your project is not web application) and create files `/WEB-INF/jetty-web.xml` and
|
||||||
|
`/WEB-INF/keycloak.json` in similar way like it's in <<fake/../classic-war.adoc#_fuse_adapter_classic_war,Classic WAR application>>.
|
||||||
|
Note you don't need `web.xml` as the security-constrains are declared in blueprint configuration file.
|
||||||
|
|
||||||
|
|
||||||
|
* The `Import-Package` in `META-INF/MANIFEST.MF` needs to contain those imports:
|
||||||
|
|
||||||
|
[source, subs="attributes"]
|
||||||
|
----
|
||||||
|
META-INF.cxf;version="[2.7,3.2)",
|
||||||
|
META-INF.cxf.osgi;version="[2.7,3.2)";resolution:=optional,
|
||||||
|
org.apache.cxf.transport.http;version="[2.7,3.2)",
|
||||||
|
org.apache.cxf.*;version="[2.7,3.2)",
|
||||||
|
com.fasterxml.jackson.jaxrs.json;version="[2.5,3)",
|
||||||
|
org.eclipse.jetty.security;version="[8,10)",
|
||||||
|
org.eclipse.jetty.util.security;version="[8,10)",
|
||||||
|
org.keycloak.*;version="{{book.project.versionMvn}}",
|
||||||
|
org.keycloak.adapters.jetty;version="{{book.project.versionMvn}}",
|
||||||
|
*;resolution:=optional
|
||||||
|
----
|
111
securing_apps/topics/oidc/java/fuse/cxf-separate.adoc
Normal file
111
securing_apps/topics/oidc/java/fuse/cxf-separate.adoc
Normal file
|
@ -0,0 +1,111 @@
|
||||||
|
|
||||||
|
[[_fuse_adapter_cxf_separate]]
|
||||||
|
===== Secure Apache CXF Endpoint on separate Jetty
|
||||||
|
|
||||||
|
It's recommended to run your CXF endpoints secured by {{book.project.name}} on separate Jetty engine. This is the setup described in this section.
|
||||||
|
|
||||||
|
* You need to add `META-INF/spring/beans.xml` to your application and then declare `httpj:engine-factory` with Jetty SecurityHandler with
|
||||||
|
injected `KeycloakJettyAuthenticator` inside. The configuration may look like this for CXF JAX-WS application:
|
||||||
|
|
||||||
|
[source,xml]
|
||||||
|
----
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xmlns:jaxws="http://cxf.apache.org/jaxws"
|
||||||
|
xmlns:httpj="http://cxf.apache.org/transports/http-jetty/configuration"
|
||||||
|
xsi:schemaLocation="
|
||||||
|
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
|
||||||
|
http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd
|
||||||
|
http://www.springframework.org/schema/osgi http://www.springframework.org/schema/osgi/spring-osgi.xsd
|
||||||
|
http://cxf.apache.org/transports/http-jetty/configuration http://cxf.apache.org/schemas/configuration/http-jetty.xsd">
|
||||||
|
|
||||||
|
<import resource="classpath:META-INF/cxf/cxf.xml" />
|
||||||
|
|
||||||
|
<bean id="kcAdapterConfig" class="org.keycloak.representations.adapters.config.AdapterConfig">
|
||||||
|
<property name="realm" value="demo"/>
|
||||||
|
<property name="resource" value="custom-cxf-endpoint"/>
|
||||||
|
<property name="bearerOnly" value="true"/>
|
||||||
|
<property name="authServerUrl" value="http://localhost:8080/auth" />
|
||||||
|
<property name="sslRequired" value="EXTERNAL"/>
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<bean id="keycloakAuthenticator" class="org.keycloak.adapters.jetty.KeycloakJettyAuthenticator">
|
||||||
|
<property name="adapterConfig">
|
||||||
|
<ref local="kcAdapterConfig" />
|
||||||
|
</property>
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<bean id="constraint" class="org.eclipse.jetty.util.security.Constraint">
|
||||||
|
<property name="name" value="Customers"/>
|
||||||
|
<property name="roles">
|
||||||
|
<list>
|
||||||
|
<value>user</value>
|
||||||
|
</list>
|
||||||
|
</property>
|
||||||
|
<property name="authenticate" value="true"/>
|
||||||
|
<property name="dataConstraint" value="0"/>
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<bean id="constraintMapping" class="org.eclipse.jetty.security.ConstraintMapping">
|
||||||
|
<property name="constraint" ref="constraint"/>
|
||||||
|
<property name="pathSpec" value="/*"/>
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<bean id="securityHandler" class="org.eclipse.jetty.security.ConstraintSecurityHandler">
|
||||||
|
<property name="authenticator" ref="keycloakAuthenticator" />
|
||||||
|
<property name="constraintMappings">
|
||||||
|
<list>
|
||||||
|
<ref local="constraintMapping" />
|
||||||
|
</list>
|
||||||
|
</property>
|
||||||
|
<property name="authMethod" value="BASIC"/>
|
||||||
|
<property name="realmName" value="does-not-matter"/>
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<httpj:engine-factory bus="cxf" id="kc-cxf-endpoint">
|
||||||
|
<httpj:engine port="8282">
|
||||||
|
<httpj:handlers>
|
||||||
|
<ref local="securityHandler" />
|
||||||
|
</httpj:handlers>
|
||||||
|
<httpj:sessionSupport>true</httpj:sessionSupport>
|
||||||
|
</httpj:engine>
|
||||||
|
</httpj:engine-factory>
|
||||||
|
|
||||||
|
<jaxws:endpoint
|
||||||
|
implementor="org.keycloak.example.ws.ProductImpl"
|
||||||
|
address="http://localhost:8282/ProductServiceCF" depends-on="kc-cxf-endpoint" />
|
||||||
|
|
||||||
|
</beans>
|
||||||
|
----
|
||||||
|
|
||||||
|
* For the CXF JAX-RS application, the only difference might be in the configuration of the endpoint dependent on engine-factory:
|
||||||
|
|
||||||
|
[source,xml]
|
||||||
|
----
|
||||||
|
<jaxrs:server serviceClass="org.keycloak.example.rs.CustomerService" address="http://localhost:8282/rest"
|
||||||
|
depends-on="kc-cxf-endpoint">
|
||||||
|
<jaxrs:providers>
|
||||||
|
<bean class="com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider" />
|
||||||
|
</jaxrs:providers>
|
||||||
|
</jaxrs:server>
|
||||||
|
----
|
||||||
|
|
||||||
|
|
||||||
|
* The `Import-Package` in `META-INF/MANIFEST.MF` needs to contain those imports:
|
||||||
|
|
||||||
|
[source, subs="attributes"]
|
||||||
|
----
|
||||||
|
META-INF.cxf;version="[2.7,3.2)",
|
||||||
|
META-INF.cxf.osgi;version="[2.7,3.2)";resolution:=optional,
|
||||||
|
org.apache.cxf.bus;version="[2.7,3.2)",
|
||||||
|
org.apache.cxf.bus.spring;version="[2.7,3.2)",
|
||||||
|
org.apache.cxf.bus.resource;version="[2.7,3.2)",
|
||||||
|
org.apache.cxf.transport.http;version="[2.7,3.2)",
|
||||||
|
org.apache.cxf.*;version="[2.7,3.2)",
|
||||||
|
org.springframework.beans.factory.config,
|
||||||
|
org.eclipse.jetty.security;version="[8,10)",
|
||||||
|
org.eclipse.jetty.util.security;version="[8,10)",
|
||||||
|
org.keycloak.*;version="{{book.project.versionMvn}}"
|
||||||
|
----
|
86
securing_apps/topics/oidc/java/fuse/fuse-admin.adoc
Normal file
86
securing_apps/topics/oidc/java/fuse/fuse-admin.adoc
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
|
||||||
|
[[_fuse_adapter_admin]]
|
||||||
|
===== Secure Fuse Admin Services
|
||||||
|
|
||||||
|
====== SSH authentication to Fuse terminal
|
||||||
|
|
||||||
|
{{book.project.name}} mainly addresses usecases for authentication of web applications, however if your other web services and applications are protected
|
||||||
|
with {{book.project.name}}, it may be good to protect non-web admin services like SSH with {{book.project.name}} credentials too. It's possible to do it
|
||||||
|
by using JAAS login module, which allows to remotely connect to {{book.project.name}} and verify credentials based on
|
||||||
|
<<fake/../../../oidc-generic.adoc#_resource_owner_password_credentials_flow,Resource Owner Password Credentials>>.
|
||||||
|
|
||||||
|
Example steps for enable SSH authentication:
|
||||||
|
|
||||||
|
* In {{book.project.name}} you need to create client (assume it's called `ssh-jmx-admin-client`), which will be used for SSH authentication.
|
||||||
|
This client needs to have switch `Direct Access Grants Enabled` to `On`.
|
||||||
|
|
||||||
|
* You need to update/specify this property in file `$FUSE_HOME/etc/org.apache.karaf.shell.cfg`:
|
||||||
|
|
||||||
|
[source]
|
||||||
|
----
|
||||||
|
sshRealm=keycloak
|
||||||
|
----
|
||||||
|
|
||||||
|
* Add file `$FUSE_HOME/etc/keycloak-direct-access.json` with the content similar to this (change based on your environment and {{book.project.name}} client settings):
|
||||||
|
|
||||||
|
[source,json]
|
||||||
|
----
|
||||||
|
{
|
||||||
|
"realm": "demo",
|
||||||
|
"resource": "ssh-jmx-admin-client",
|
||||||
|
"ssl-required" : "external",
|
||||||
|
"auth-server-url" : "http://localhost:8080/auth",
|
||||||
|
"credentials": {
|
||||||
|
"secret": "password"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
----
|
||||||
|
This file contains configuration of the client application, which is used by JAAS DirectAccessGrantsLoginModule from `keycloak` JAAS realm for SSH authentication.
|
||||||
|
|
||||||
|
* Start Fuse and install `keycloak` JAAS realm into Fuse. This could be done easily by installing `keycloak-jaas` feature, which has JAAS realm predefined
|
||||||
|
(you are able to override it by using your own `keycloak` JAAS realm with higher ranking. See JBoss Fuse documentation for more details). Use those commands in Fuse terminal:
|
||||||
|
|
||||||
|
[source, subs="attributes"]
|
||||||
|
----
|
||||||
|
features:addurl mvn:org.keycloak/keycloak-osgi-features/{{book.project.versionMvn}}/xml/features
|
||||||
|
features:install keycloak-jaas
|
||||||
|
----
|
||||||
|
|
||||||
|
* Now let's type this from your terminal to login via SSH as `admin` user:
|
||||||
|
|
||||||
|
```
|
||||||
|
ssh -o PubkeyAuthentication=no -p 8101 admin@localhost
|
||||||
|
```
|
||||||
|
|
||||||
|
And login with password `password`.
|
||||||
|
|
||||||
|
NOTE: On some newer operating systems, you may also need to use this option of SSH command `-o HostKeyAlgorithms=+ssh-dss` because newer SSH clients
|
||||||
|
don't allow to use `ssh-dss` algorithm by default, but it's currently used by default in {{book.fuseVersion}} .
|
||||||
|
|
||||||
|
|
||||||
|
Note that your user needs to have realm role `admin` if he wants to do everything or some other roles to be able to do just subset of operations
|
||||||
|
(eg. role `viewer` to be able to run just read-only Karaf commands) . The available roles are configured in `$FUSE_HOME/etc/org.apache.karaf.shell.cfg` or `$FUSE_HOME/etc/system.properties` .
|
||||||
|
|
||||||
|
|
||||||
|
====== JMX authentication
|
||||||
|
|
||||||
|
This may be needed in case if you really want to use jconsole or other external tool to perform remote connection to JMX through RMI. Otherwise it may
|
||||||
|
be better to use just hawt.io/jolokia as jolokia agent is installed in hawt.io by default. See <<fake/../hawtio.adoc#_hawtio,Hawtio Admin Console>> section for more details.
|
||||||
|
|
||||||
|
* In file `$FUSE_HOME/etc/org.apache.karaf.management.cfg` you can change this property:
|
||||||
|
|
||||||
|
[source]
|
||||||
|
----
|
||||||
|
jmxRealm=keycloak
|
||||||
|
----
|
||||||
|
|
||||||
|
* You need `keycloak-jaas` feature and file `$FUSE_HOME/etc/keycloak-direct-access.json` as described in SSH section above.
|
||||||
|
|
||||||
|
* In jconsole you can fill URL like:
|
||||||
|
|
||||||
|
[source]
|
||||||
|
----
|
||||||
|
service:jmx:rmi://localhost:44444/jndi/rmi://localhost:1099/karaf-root
|
||||||
|
----
|
||||||
|
|
||||||
|
and credentials: admin/password (based on the user with admin privileges according to your environment)
|
129
securing_apps/topics/oidc/java/fuse/hawtio.adoc
Normal file
129
securing_apps/topics/oidc/java/fuse/hawtio.adoc
Normal file
|
@ -0,0 +1,129 @@
|
||||||
|
|
||||||
|
[[_hawtio]]
|
||||||
|
===== Secure Hawtio Admin Console
|
||||||
|
|
||||||
|
The steps to secure Hawtio Admin Console with {{book.project.name}} are:
|
||||||
|
|
||||||
|
* Add these properties to the file `$FUSE_HOME/etc/system.properties` :
|
||||||
|
|
||||||
|
[source]
|
||||||
|
----
|
||||||
|
hawtio.keycloakEnabled=true
|
||||||
|
hawtio.realm=keycloak
|
||||||
|
hawtio.keycloakClientConfig=${karaf.base}/etc/keycloak-hawtio-client.json
|
||||||
|
hawtio.rolePrincipalClasses=org.keycloak.adapters.jaas.RolePrincipal,org.apache.karaf.jaas.boot.principal.RolePrincipal
|
||||||
|
----
|
||||||
|
|
||||||
|
* Create client in {{book.project.name}} admin console in your realm. Assuming you have {{book.project.name}} realm `demo` and you created client `hawtio-client`, which is marked
|
||||||
|
with `public` Access Type and has redirect URI pointing to Hawtio - http://localhost:8181/hawtio/* .
|
||||||
|
|
||||||
|
* Create file `keycloak-hawtio-client.json` in the directory `$FUSE_HOME/etc` and use the content like this (Change properties `realm`, `resource` and `auth-server-url` according to
|
||||||
|
your {{book.project.name}} environment. The property `resource` should point to the client created in previous step. This file is used by the client (Hawtio Javascript application) side.
|
||||||
|
|
||||||
|
[source,json]
|
||||||
|
----
|
||||||
|
{
|
||||||
|
"realm" : "demo",
|
||||||
|
"resource" : "hawtio-client",
|
||||||
|
"auth-server-url" : "http://localhost:8080/auth",
|
||||||
|
"ssl-required" : "external",
|
||||||
|
"public-client" : true
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
|
* Create file `keycloak-hawtio.json` in the directory `$FUSE_HOME/etc` and use the content like this (Change properties `realm` and `auth-server-url` according to
|
||||||
|
your {{book.project.name}} environment. This file is used by the adapters on server (JAAS Login module) side.
|
||||||
|
|
||||||
|
|
||||||
|
[source,json]
|
||||||
|
----
|
||||||
|
{
|
||||||
|
"realm" : "demo",
|
||||||
|
"resource" : "jaas",
|
||||||
|
"bearer-only" : true,
|
||||||
|
"auth-server-url" : "http://localhost:8080/auth",
|
||||||
|
"ssl-required" : "external",
|
||||||
|
"use-resource-role-mappings": false,
|
||||||
|
"principal-attribute": "preferred_username"
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
|
* Start {{book.fuseVersion}} and install feature `keycloak` if you didn't already. The commands in Karaf terminal are like:
|
||||||
|
|
||||||
|
|
||||||
|
[source, subs="attributes"]
|
||||||
|
----
|
||||||
|
features:addurl mvn:org.keycloak/keycloak-osgi-features/{{book.project.versionMvn}}/xml/features
|
||||||
|
features:install keycloak
|
||||||
|
----
|
||||||
|
|
||||||
|
* Go to http://localhost:8181/hawtio and login as some user from your {{book.project.name}} realm. See file `$FUSE_HOME/etc/system.properties` and property `hawtio.roles` .
|
||||||
|
Just those with some of the role are able to successfully authenticate to Hawtio and do something here.
|
||||||
|
|
||||||
|
|
||||||
|
====== Secure Hawtio on EAP
|
||||||
|
|
||||||
|
This subsection contains needed steps for the case, when you want to run Hawtio on the Wildfly 10 server.
|
||||||
|
|
||||||
|
* Setup {{book.project.name}} similarly like mentioned above in the section for securing Hawtion on JBoss Fuse. So assuming you have {{book.project.name}} realm `demo`
|
||||||
|
and client `hawtio-client`. Assumption is that your {{book.project.name}} is running on `localhost:8080` when the Wildfly with deployed hawtio will be running on `localhost:8181`.
|
||||||
|
|
||||||
|
* Copy `hawtio.war` to the `$WILDFLY_HOME/standalone/configuration` directory. See Hawtio/Fuse documentation for more details about the Hawtio deployment.
|
||||||
|
|
||||||
|
* Copy files `keycloak-hawtio.json` and `keycloak-hawtio-client.json` with the above content to the `$WILDFLY_HOME/standalone/configuration` directory.
|
||||||
|
|
||||||
|
* Install {{book.project.name}} adapter subsystem to your Wildfly as described in the <<fake/../../jboss-adapter.adoc#_jboss_adapter,JBoss adapter documentation>>
|
||||||
|
|
||||||
|
* In `$WILDFLY_HOME/standalone/configuration/standalone.xml` configure system properties like this:
|
||||||
|
|
||||||
|
[source,xml]
|
||||||
|
----
|
||||||
|
<extensions>
|
||||||
|
...
|
||||||
|
</extensions>
|
||||||
|
|
||||||
|
<system-properties>
|
||||||
|
<property name="hawtio.authenticationEnabled" value="true" />
|
||||||
|
<property name="hawtio.realm" value="hawtio" />
|
||||||
|
<property name="hawtio.roles" value="admin,viewer" />
|
||||||
|
<property name="hawtio.rolePrincipalClasses" value="org.keycloak.adapters.jaas.RolePrincipal" />
|
||||||
|
<property name="hawtio.keycloakEnabled" value="true" />
|
||||||
|
<property name="hawtio.keycloakClientConfig" value="${jboss.server.config.dir}/keycloak-hawtio-client.json" />
|
||||||
|
<property name="hawtio.keycloakServerConfig" value="${jboss.server.config.dir}/keycloak-hawtio.json" />
|
||||||
|
</system-properties>
|
||||||
|
----
|
||||||
|
|
||||||
|
Also add hawtio realm to this file to the `security-domains` section:
|
||||||
|
|
||||||
|
[source,xml]
|
||||||
|
----
|
||||||
|
<security-domain name="hawtio" cache-type="default">
|
||||||
|
<authentication>
|
||||||
|
<login-module code="org.keycloak.adapters.jaas.BearerTokenLoginModule" flag="required">
|
||||||
|
<module-option name="keycloak-config-file" value="${hawtio.keycloakServerConfig}"/>
|
||||||
|
</login-module>
|
||||||
|
</authentication>
|
||||||
|
</security-domain>
|
||||||
|
----
|
||||||
|
|
||||||
|
Finally add the `secure-deployment` section `hawtio` to the adapter subsystem. It should ensure that Hawtio WAR is able to find the JAAS login module classes.
|
||||||
|
|
||||||
|
|
||||||
|
[source,xml]
|
||||||
|
----
|
||||||
|
<subsystem xmlns="urn:jboss:domain:keycloak:1.1">
|
||||||
|
<secure-deployment name="hawtio.war" />
|
||||||
|
</subsystem>
|
||||||
|
----
|
||||||
|
|
||||||
|
* Restart Wildfly server with Hawtio
|
||||||
|
|
||||||
|
[source,xml]
|
||||||
|
----
|
||||||
|
cd $WILDFLY_HOME/bin
|
||||||
|
./standalone.sh -Djboss.socket.binding.port-offset=101
|
||||||
|
----
|
||||||
|
|
||||||
|
* Access Hawtio on http://localhost:8181/hawtio . It will be secured by the {{book.project.name}} .
|
||||||
|
|
||||||
|
|
79
securing_apps/topics/oidc/java/fuse/install-feature.adoc
Normal file
79
securing_apps/topics/oidc/java/fuse/install-feature.adoc
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
|
||||||
|
[[_fuse_install_feature]]
|
||||||
|
===== Install Feature
|
||||||
|
|
||||||
|
First thing to be done is to install the feature `keycloak` into the JBoss Fuse environment. This will install the Fuse adapter
|
||||||
|
together with all needed 3rd party dependencies. There are 2 possibilities to install it.
|
||||||
|
|
||||||
|
|
||||||
|
====== Install from Maven Repository
|
||||||
|
|
||||||
|
You need to be online and have access to the maven repository.
|
||||||
|
|
||||||
|
{% if book.community %}
|
||||||
|
For community it's sufficient to be online as all the artifacts and 3rd party dependencies should be available in maven central repository.
|
||||||
|
{% endif %}
|
||||||
|
{% if book.product %}
|
||||||
|
For {{book.project.name}} you first need to configure proper maven repository, so you can install the artifacts. You can find on
|
||||||
|
https://access.redhat.com/maven-repository[Maven Repository] page what's the proper maven repository containing the bits.
|
||||||
|
|
||||||
|
Assuming it's https://maven.repository.redhat.com/ga/ you will need to add this to the file `$FUSE_HOME/etc/org.ops4j.pax.url.mvn.cfg`
|
||||||
|
and add the repository to the list of supported repositories. For example it may look like this:
|
||||||
|
|
||||||
|
[source]
|
||||||
|
----
|
||||||
|
org.ops4j.pax.url.mvn.repositories= \
|
||||||
|
https://maven.repository.redhat.com/ga@id=redhat.product.repo
|
||||||
|
http://repo1.maven.org/maven2@id=maven.central.repo, \
|
||||||
|
...
|
||||||
|
----
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
You need to start {{book.fuseVersion}} and then in the Karaf terminal you type this:
|
||||||
|
|
||||||
|
[source,subs="attributes"]
|
||||||
|
----
|
||||||
|
features:addurl mvn:org.keycloak/keycloak-osgi-features/{{book.project.versionMvn}}/xml/features
|
||||||
|
features:install keycloak
|
||||||
|
----
|
||||||
|
|
||||||
|
Then you may need to install Jetty 9 feature:
|
||||||
|
|
||||||
|
[source]
|
||||||
|
----
|
||||||
|
features:install keycloak-jetty9-adapter
|
||||||
|
----
|
||||||
|
|
||||||
|
NOTE: If you are on JBoss Fuse 6.2 or older, you should use `keycloak-jetty8-adapter` . However it's highly recommended to
|
||||||
|
rather upgrade to {{book.fuseVersion}} instead.
|
||||||
|
|
||||||
|
Then you can check that requested features were installed:
|
||||||
|
|
||||||
|
[source]
|
||||||
|
----
|
||||||
|
features:list | grep keycloak
|
||||||
|
----
|
||||||
|
|
||||||
|
====== Install from ZIP bundle
|
||||||
|
|
||||||
|
This is useful if you are offline and/or don't want to use maven for download jar files and other artifacts. Once you download ZIP bundle of {{book.project.name}} Fuse adapter,
|
||||||
|
you will need to unzip it into the root directory of JBoss Fuse. This should install the dependencies under the `system` directory. Use this for {{book.fuseVersion}} :
|
||||||
|
|
||||||
|
[source,subs="attributes"]
|
||||||
|
----
|
||||||
|
cd /path-to-fuse/jboss-fuse-6.3.0.redhat-198
|
||||||
|
unzip -q /path-to-adapter-zip/keycloak-fuse-adapter-dist-{{book.project.versionMvn}}.zip
|
||||||
|
----
|
||||||
|
|
||||||
|
Feel free to overwrite all already existing jars. Once you unzip archive, you can start the Fuse and again run commands in fuse/karaf terminal
|
||||||
|
|
||||||
|
[source,subs="attributes"]
|
||||||
|
----
|
||||||
|
features:addurl mvn:org.keycloak/keycloak-osgi-features/{{book.project.versionMvn}}/xml/features
|
||||||
|
features:install keycloak
|
||||||
|
----
|
||||||
|
|
||||||
|
And also install the corresponding Jetty adapter. The difference from the previous part is, that nothing will be downloaded from maven repository as the artifacts were
|
||||||
|
available directly in JBoss Fuse `system` directory.
|
||||||
|
|
||||||
|
|
78
securing_apps/topics/oidc/java/fuse/servlet-whiteboard.adoc
Normal file
78
securing_apps/topics/oidc/java/fuse/servlet-whiteboard.adoc
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
|
||||||
|
[[_fuse_adapter_servlet_whiteboard]]
|
||||||
|
===== Secure Servlet deployed as OSGI service
|
||||||
|
|
||||||
|
This is useful for the case, when you have sevlet class inside your OSGI bundle project, which is not deployed as classic WAR. Fuse uses
|
||||||
|
https://ops4j1.jira.com/wiki/display/ops4j/Pax+Web+Extender+-+Whiteboard[Pax Web Whiteboard Extender] for deploy such servlet as web application.
|
||||||
|
|
||||||
|
The needed steps to secure your servlet with {{book.project.name}} are:
|
||||||
|
|
||||||
|
* {{book.project.name}} provides PaxWebIntegrationService, which allows to inject jetty-web.xml and configure security constraints for your application.
|
||||||
|
You need to declare such service in `OSGI-INF/blueprint/blueprint.xml` inside your application. Note that your servlet needs to depend on it.
|
||||||
|
The example configuration can look like this:
|
||||||
|
|
||||||
|
[source,xml]
|
||||||
|
----
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://www.osgi.org/xmlns/blueprint/v1.0.0
|
||||||
|
http://www.osgi.org/xmlns/blueprint/v1.0.0/blueprint.xsd">
|
||||||
|
|
||||||
|
<!-- Using jetty bean just for the compatibility with other fuse services -->
|
||||||
|
<bean id="servletConstraintMapping" class="org.eclipse.jetty.security.ConstraintMapping">
|
||||||
|
<property name="constraint">
|
||||||
|
<bean class="org.eclipse.jetty.util.security.Constraint">
|
||||||
|
<property name="name" value="cst1"/>
|
||||||
|
<property name="roles">
|
||||||
|
<list>
|
||||||
|
<value>user</value>
|
||||||
|
</list>
|
||||||
|
</property>
|
||||||
|
<property name="authenticate" value="true"/>
|
||||||
|
<property name="dataConstraint" value="0"/>
|
||||||
|
</bean>
|
||||||
|
</property>
|
||||||
|
<property name="pathSpec" value="/product-portal/*"/>
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<bean id="keycloakPaxWebIntegration" class="org.keycloak.adapters.osgi.PaxWebIntegrationService"
|
||||||
|
init-method="start" destroy-method="stop">
|
||||||
|
<property name="jettyWebXmlLocation" value="/WEB-INF/jetty-web.xml" />
|
||||||
|
<property name="bundleContext" ref="blueprintBundleContext" />
|
||||||
|
<property name="constraintMappings">
|
||||||
|
<list>
|
||||||
|
<ref component-id="servletConstraintMapping" />
|
||||||
|
</list>
|
||||||
|
</property>
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<bean id="productServlet" class="org.keycloak.example.ProductPortalServlet" depends-on="keycloakPaxWebIntegration">
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<service ref="productServlet" interface="javax.servlet.Servlet">
|
||||||
|
<service-properties>
|
||||||
|
<entry key="alias" value="/product-portal" />
|
||||||
|
<entry key="servlet-name" value="ProductServlet" />
|
||||||
|
<entry key="keycloak.config.file" value="/keycloak.json" />
|
||||||
|
</service-properties>
|
||||||
|
</service>
|
||||||
|
|
||||||
|
</blueprint>
|
||||||
|
----
|
||||||
|
|
||||||
|
* You may need to have directory `WEB-INF` inside your project (even if your project is not web application) and create files `/WEB-INF/jetty-web.xml` and
|
||||||
|
`/WEB-INF/keycloak.json` in similar way like it's in <<fake/../classic-war.adoc#_fuse_adapter_classic_war,Classic WAR application>>.
|
||||||
|
Note you don't need `web.xml` as the security-constrains are declared in blueprint configuration file.
|
||||||
|
|
||||||
|
* The `Import-Package` in `META-INF/MANIFEST.MF` needs to contain at least those imports:
|
||||||
|
|
||||||
|
[source, subs="attributes"]
|
||||||
|
----
|
||||||
|
org.keycloak.adapters.jetty;version="{{book.project.versionMvn}}",
|
||||||
|
org.keycloak.adapters;version="{{book.project.versionMvn}}",
|
||||||
|
org.keycloak.constants;version="{{book.project.versionMvn}}",
|
||||||
|
org.keycloak.util;version="{{book.project.versionMvn}}",
|
||||||
|
org.keycloak.*;version="{{book.project.versionMvn}}",
|
||||||
|
*;resolution:=optional
|
||||||
|
----
|
31
securing_apps/topics/oidc/java/jaas.adoc
Normal file
31
securing_apps/topics/oidc/java/jaas.adoc
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
[[_jaas_adapter]]
|
||||||
|
==== JAAS plugin
|
||||||
|
|
||||||
|
It's generally not needed to use JAAS for most of the applications, especially if they are HTTP based, and you should most likely choose one of our other adapters.
|
||||||
|
However, some applications and systems may still rely on pure legacy JAAS solution.
|
||||||
|
{{book.project.name}} provides two login modules to help in these situations.
|
||||||
|
|
||||||
|
The provided login modules are:
|
||||||
|
|
||||||
|
org.keycloak.adapters.jaas.DirectAccessGrantsLoginModule::
|
||||||
|
This login module allows to authenticate with username/password from {{book.project.name}}.
|
||||||
|
It's using <<fake/../../oidc-generic.adoc#_resource_owner_password_credentials_flow,Resource Owner Password Credentials>> flow to validate if the provided
|
||||||
|
username/password is valid. It's useful for non-web based systems, which need to rely on JAAS and want to use {{book.project.name}}, but can't use the standard browser
|
||||||
|
based flows due to their non-web nature. Example of such application could be messaging or SSH.
|
||||||
|
|
||||||
|
org.keycloak.adapters.jaas.BearerTokenLoginModule::
|
||||||
|
This login module allows to authenticate with {{book.project.name}} access token passed to it through CallbackHandler as password.
|
||||||
|
It may be useful for example in case, when you have {{book.project.name}} access token from standard based authentication flow and your web application then
|
||||||
|
needs to talk to external non-web based system, which rely on JAAS. For example a messaging system.
|
||||||
|
|
||||||
|
Both modules use the following configuration properties:
|
||||||
|
|
||||||
|
keycloak-config-file::
|
||||||
|
The location of the `keycloak.json` configuration file. The configuration file can either be located on the filesystem or on the classpath. If it's located
|
||||||
|
on the classpath you need to prefix the location with `classpath:` (for example `classpath:/path/keycloak.json`).
|
||||||
|
This is _REQUIRED._
|
||||||
|
|
||||||
|
`role-principal-class`::
|
||||||
|
Configure alternative class for Role principals attached to JAAS Subject.
|
||||||
|
Default value is `org.keycloak.adapters.jaas.RolePrincipal`. Note: The class is required to have a constructor with a single `String` argument.
|
||||||
|
|
227
securing_apps/topics/oidc/java/java-adapter-config.adoc
Normal file
227
securing_apps/topics/oidc/java/java-adapter-config.adoc
Normal file
|
@ -0,0 +1,227 @@
|
||||||
|
|
||||||
|
[[_java_adapter_config]]
|
||||||
|
==== Java Adapter Config
|
||||||
|
|
||||||
|
Each Java adapter supported by {{book.project.name}} can be configured by a simple JSON file.
|
||||||
|
This is what one might look like:
|
||||||
|
|
||||||
|
[source,json]
|
||||||
|
----
|
||||||
|
{
|
||||||
|
"realm" : "demo",
|
||||||
|
"resource" : "customer-portal",
|
||||||
|
"realm-public-key" : "MIGfMA0GCSqGSIb3D...31LwIDAQAB",
|
||||||
|
"auth-server-url" : "https://localhost:8443/auth",
|
||||||
|
"ssl-required" : "external",
|
||||||
|
"use-resource-role-mappings" : false,
|
||||||
|
"enable-cors" : true,
|
||||||
|
"cors-max-age" : 1000,
|
||||||
|
"cors-allowed-methods" : "POST, PUT, DELETE, GET",
|
||||||
|
"bearer-only" : false,
|
||||||
|
"enable-basic-auth" : false,
|
||||||
|
"expose-token" : true,
|
||||||
|
"credentials" : {
|
||||||
|
"secret" : "234234-234234-234234"
|
||||||
|
},
|
||||||
|
|
||||||
|
"connection-pool-size" : 20,
|
||||||
|
"disable-trust-manager": false,
|
||||||
|
"allow-any-hostname" : false,
|
||||||
|
"truststore" : "path/to/truststore.jks",
|
||||||
|
"truststore-password" : "geheim",
|
||||||
|
"client-keystore" : "path/to/client-keystore.jks",
|
||||||
|
"client-keystore-password" : "geheim",
|
||||||
|
"client-key-password" : "geheim",
|
||||||
|
"token-minimum-time-to-live" : 10,
|
||||||
|
"min-time-between-jwks-requests" : 10,
|
||||||
|
"public-key-cache-ttl": 86400
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
|
You can use `${...}` enclosure for system property replacement. For example `${jboss.server.config.dir}` would be replaced by `/path/to/{{book.project.name}}`.
|
||||||
|
Replacement of environment variables is also supported via the `env` prefix, e.g. `${env.MY_ENVIRONMENT_VARIABLE}`.
|
||||||
|
|
||||||
|
The initial config file can be obtained from the the admin console. This can be done by opening the admin console, select `Clients` from the menu and clicking
|
||||||
|
on the corresponding client. Once the page for the client is opened click on the `Installation` tab and select `Keycloak OIDC JSON`.
|
||||||
|
|
||||||
|
Here is a description of each configuration option:
|
||||||
|
|
||||||
|
realm::
|
||||||
|
Name of the realm.
|
||||||
|
This is _REQUIRED._
|
||||||
|
|
||||||
|
resource::
|
||||||
|
The client-id of the application. Each application has a client-id that is used to identify the application.
|
||||||
|
This is _REQUIRED._
|
||||||
|
|
||||||
|
realm-public-key::
|
||||||
|
PEM format of the realm public key. You can obtain this from the administration console.
|
||||||
|
This is _OPTIONAL_ and it's not recommended to set it. If not set, the adapter will download this from {{book.project.name}} and
|
||||||
|
it will always re-download it when needed (eg. {{book.project.name}} rotate it's keys). However if realm-public-key is set, then adapter
|
||||||
|
will never download new keys from {{book.project.name}}, so when {{book.project.name}} rotate it's keys, adapter will break.
|
||||||
|
|
||||||
|
auth-server-url::
|
||||||
|
The base URL of the {{book.project.name}} server. All other {{book.project.name}} pages and REST service endpoints are derived from this. It is usually of the form `$$https://host:port/auth$$`.
|
||||||
|
This is _REQUIRED._
|
||||||
|
|
||||||
|
ssl-required::
|
||||||
|
Ensures that all communication to and from the {{book.project.name}} server is over HTTPS.
|
||||||
|
In production this should be set to `all`.
|
||||||
|
This is _OPTIONAL_.
|
||||||
|
The default value is _external_ meaning that HTTPS is required by default for external requests.
|
||||||
|
Valid values are 'all', 'external' and 'none'.
|
||||||
|
|
||||||
|
use-resource-role-mappings::
|
||||||
|
If set to true, the adapter will look inside the token for application level role mappings for the user. If false, it will look at the realm level for user role mappings.
|
||||||
|
This is _OPTIONAL_.
|
||||||
|
The default value is _false_.
|
||||||
|
|
||||||
|
public-client::
|
||||||
|
If set to true, the adapter will not send credentials for the client to {{book.project.name}}.
|
||||||
|
This is _OPTIONAL_.
|
||||||
|
The default value is _false_.
|
||||||
|
|
||||||
|
enable-cors::
|
||||||
|
This enables CORS support. It will handle CORS preflight requests. It will also look into the access token to determine valid origins.
|
||||||
|
This is _OPTIONAL_.
|
||||||
|
The default value is _false_.
|
||||||
|
|
||||||
|
cors-max-age::
|
||||||
|
If CORS is enabled, this sets the value of the `Access-Control-Max-Age` header.
|
||||||
|
This is _OPTIONAL_.
|
||||||
|
If not set, this header is not returned in CORS responses.
|
||||||
|
|
||||||
|
cors-allowed-methods::
|
||||||
|
If CORS is enabled, this sets the value of the `Access-Control-Allow-Methods` header.
|
||||||
|
This should be a comma-separated string.
|
||||||
|
This is _OPTIONAL_.
|
||||||
|
If not set, this header is not returned in CORS responses.
|
||||||
|
|
||||||
|
cors-allowed-headers::
|
||||||
|
If CORS is enabled, this sets the value of the `Access-Control-Allow-Headers` header.
|
||||||
|
This should be a comma-separated string.
|
||||||
|
This is _OPTIONAL_.
|
||||||
|
If not set, this header is not returned in CORS responses.
|
||||||
|
|
||||||
|
bearer-only::
|
||||||
|
This should be set to _true_ for services. If enabled the adapter will not attempt to authenticate users, but only verify bearer tokens.
|
||||||
|
This is _OPTIONAL_.
|
||||||
|
The default value is _false_.
|
||||||
|
|
||||||
|
autodetect-bearer-only::
|
||||||
|
This should be set to __true__ if your application serves both a web application and web services (e.g. SOAP or REST).
|
||||||
|
It allows you to redirect unauthenticated users of the web application to the Keycloak login page,
|
||||||
|
but send an HTTP `401` status code to unauthenticated SOAP or REST clients instead as they would not understand a redirect to the login page.
|
||||||
|
Keycloak auto-detects SOAP or REST clients based on typical headers like `X-Requested-With`, `SOAPAction` or `Accept`.
|
||||||
|
The default value is _false_.
|
||||||
|
|
||||||
|
enable-basic-auth::
|
||||||
|
This tells the adapter to also support basic authentication. If this option is enabled, then _secret_ must also be provided.
|
||||||
|
This is _OPTIONAL_.
|
||||||
|
The default value is _false_.
|
||||||
|
|
||||||
|
expose-token::
|
||||||
|
If `true`, an authenticated browser client (via a Javascript HTTP invocation) can obtain the signed access token via the URL `root/k_query_bearer_token`.
|
||||||
|
This is _OPTIONAL_.
|
||||||
|
The default value is _false_.
|
||||||
|
|
||||||
|
credentials::
|
||||||
|
Specify the credentials of the application. This is an object notation where the key is the credential type and the value is the value of the credential type.
|
||||||
|
Currently password and jwt is supported. This is _REQUIRED_ only for clients with 'Confidential' access type.
|
||||||
|
|
||||||
|
connection-pool-size::
|
||||||
|
Adapters will make separate HTTP invocations to the {{book.project.name}} server to turn an access code into an access token.
|
||||||
|
This config option defines how many connections to the {{book.project.name}} server should be pooled.
|
||||||
|
This is _OPTIONAL_.
|
||||||
|
The default value is `20`.
|
||||||
|
|
||||||
|
disable-trust-manager::
|
||||||
|
If the {{book.project.name}} server requires HTTPS and this config option is set to `true` you do not have to specify a truststore.
|
||||||
|
This setting should only be used during development and *never* in production as it will disable verification of SSL certificates.
|
||||||
|
This is _OPTIONAL_.
|
||||||
|
The default value is `false`.
|
||||||
|
|
||||||
|
allow-any-hostname::
|
||||||
|
If the {{book.project.name}} server requires HTTPS and this config option is set to `true` the {{book.project.name}} server's certificate is validated via the truststore,
|
||||||
|
but host name validation is not done.
|
||||||
|
This setting should only be used during development and *never* in production as it will disable verification of SSL certificates.
|
||||||
|
This seting may be useful in test environments This is _OPTIONAL_.
|
||||||
|
The default value is `false`.
|
||||||
|
|
||||||
|
proxy-url::
|
||||||
|
The URL for the HTTP proxy if one is used.
|
||||||
|
|
||||||
|
truststore::
|
||||||
|
The value is the file path to a keystore file.
|
||||||
|
If you prefix the path with `classpath:`, then the truststore will be obtained from the deployment's classpath instead.
|
||||||
|
Used for outgoing HTTPS communications to the {{book.project.name}} server.
|
||||||
|
Client making HTTPS requests need a way to verify the host of the server they are talking to.
|
||||||
|
This is what the trustore does.
|
||||||
|
The keystore contains one or more trusted host certificates or certificate authorities.
|
||||||
|
You can create this truststore by extracting the public certificate of the {{book.project.name}} server's SSL keystore.
|
||||||
|
This is _REQUIRED_ unless `ssl-required` is `none` or `disable-trust-manager` is `true`.
|
||||||
|
|
||||||
|
truststore-password::
|
||||||
|
Password for the truststore keystore.
|
||||||
|
This is _REQUIRED_ if `truststore` is set and the truststore requires a password.
|
||||||
|
|
||||||
|
client-keystore::
|
||||||
|
This is the file path to a keystore file.
|
||||||
|
This keystore contains client certificate for two-way SSL when the adapter makes HTTPS requests to the {{book.project.name}} server.
|
||||||
|
This is _OPTIONAL_.
|
||||||
|
|
||||||
|
client-keystore-password::
|
||||||
|
Password for the client keystore.
|
||||||
|
This is _REQUIRED_ if `client-keystore` is set.
|
||||||
|
|
||||||
|
client-key-password::
|
||||||
|
Password for the client's key.
|
||||||
|
This is _REQUIRED_ if `client-keystore` is set.
|
||||||
|
|
||||||
|
always-refresh-token::
|
||||||
|
If _true_, the adapter will refresh token in every request.
|
||||||
|
|
||||||
|
register-node-at-startup::
|
||||||
|
If _true_, then adapter will send registration request to {{book.project.name}}.
|
||||||
|
It's _false_ by default and useful only when application is clustered.
|
||||||
|
See <<fake/../../oid/java/application-clustering.adoc#_applicationclustering,Application Clustering>> for details
|
||||||
|
|
||||||
|
register-node-period::
|
||||||
|
Period for re-registration adapter to {{book.project.name}}.
|
||||||
|
Useful when application is clustered.
|
||||||
|
See <<fake/../../oid/java/application-clustering.adoc#_applicationclustering,Application Clustering>> for details
|
||||||
|
|
||||||
|
token-store::
|
||||||
|
Possible values are _session_ and _cookie_.
|
||||||
|
Default is _session_, which means that adapter stores account info in HTTP Session.
|
||||||
|
Alternative _cookie_ means storage of info in cookie.
|
||||||
|
See <<fake/../../oid/java/application-clustering.adoc#_applicationclustering,Application Clustering>> for details
|
||||||
|
|
||||||
|
principal-attribute::
|
||||||
|
OpenID Connection ID Token attribute to populate the UserPrincipal name with.
|
||||||
|
If token attribute is null, defaults to `sub`.
|
||||||
|
Possible values are `sub`, `preferred_username`, `email`, `name`, `nickname`, `given_name`, `family_name`.
|
||||||
|
|
||||||
|
turn-off-change-session-id-on-login::
|
||||||
|
The session id is changed by default on a successful login on some platforms to plug a security attack vector. Change this to true if you want to turn this off This is _OPTIONAL_.
|
||||||
|
The default value is _false_.
|
||||||
|
|
||||||
|
token-minimum-time-to-live::
|
||||||
|
Amount of time, in seconds, to preemptively refresh an active access token with the {{book.project.name}} server before it expires.
|
||||||
|
This is especially useful when the access token is sent to another REST client where it could expire before being evaluated.
|
||||||
|
This value should never exceed the realm's access token lifespan.
|
||||||
|
This is _OPTIONAL_. The default value is `0` seconds, so adapter will refresh access token just if it's expired.
|
||||||
|
|
||||||
|
min-time-between-jwks-requests::
|
||||||
|
Amount of time, in seconds, specifying minimum interval between two requests to {{book.project.name}} to retrieve new public keys.
|
||||||
|
It is 10 seconds by default.
|
||||||
|
Adapter will always try to download new public key when it recognize token with unknown `kid` . However it won't try it more
|
||||||
|
than once per 10 seconds (by default). This is to avoid DoS when attacker sends lots of tokens with bad `kid` forcing adapter
|
||||||
|
to send lots of requests to {{book.project.name}}.
|
||||||
|
|
||||||
|
public-key-cache-ttl::
|
||||||
|
Amount of time, in seconds, specifying maximum interval between two requests to {{book.project.name}} to retrieve new public keys.
|
||||||
|
It is 86400 seconds (1 day) by default.
|
||||||
|
Adapter will always try to download new public key when it recognize token with unknown `kid` . If it recognize token with known `kid`, it will
|
||||||
|
just use the public key downloaded previously. However at least once per this configured interval (1 day by default) will be new
|
||||||
|
public key always downloaded even if the `kid` of token is already known.
|
5
securing_apps/topics/oidc/java/java-adapters.adoc
Normal file
5
securing_apps/topics/oidc/java/java-adapters.adoc
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
=== Java Adapters
|
||||||
|
|
||||||
|
{{book.project.name}} comes with a range of different adapters for Java application. Selecting the correct adapter depends on the target platform.
|
||||||
|
|
||||||
|
All Java adapters share a set of common configuration options described in the <<fake/../java-adapter-config.adoc#_java_adapter_config,Java Adapters Config>> chapter.
|
252
securing_apps/topics/oidc/java/jboss-adapter.adoc
Normal file
252
securing_apps/topics/oidc/java/jboss-adapter.adoc
Normal file
|
@ -0,0 +1,252 @@
|
||||||
|
[[_jboss_adapter]]
|
||||||
|
|
||||||
|
{% if book.community %}
|
||||||
|
==== JBoss EAP/Wildfly Adapter
|
||||||
|
{% endif %}
|
||||||
|
{% if book.product %}
|
||||||
|
==== JBoss EAP Adapter
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if book.community %}
|
||||||
|
To be able to secure WAR apps deployed on JBoss EAP, WildFly or JBoss AS, you must install and configure the
|
||||||
|
{{book.project.name}} adapter subsystem. You then have two options to secure your WARs.
|
||||||
|
{% endif %}
|
||||||
|
{% if book.product %}
|
||||||
|
To be able to secure WAR apps deployed on JBoss EAP, you must install and configure the
|
||||||
|
{{book.project.name}} adapter subsystem. You then have two options to secure your WARs.
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
You can provide an adapter config file in your WAR and change the auth-method to KEYCLOAK within web.xml.
|
||||||
|
|
||||||
|
Alternatively, you don't have to modify your WAR at all and you can secure it via the {{book.project.name}} adapter subsystem configuration in `standalone.xml`.
|
||||||
|
Both methods are described in this section.
|
||||||
|
|
||||||
|
[[_jboss_adapter_installation]]
|
||||||
|
===== Installing the adapter
|
||||||
|
|
||||||
|
Adapters are available as a separate archive depending on what server version you are using.
|
||||||
|
|
||||||
|
{% if book.community %}
|
||||||
|
Install on Wildfly 9 or 10:
|
||||||
|
|
||||||
|
[source, subs="attributes"]
|
||||||
|
----
|
||||||
|
$ cd $WILDFLY_HOME
|
||||||
|
$ unzip keycloak-wildfly-adapter-dist-{{book.project.version}}.zip
|
||||||
|
----
|
||||||
|
|
||||||
|
Install on Wildfly 8:
|
||||||
|
|
||||||
|
[source, subs="attributes"]
|
||||||
|
----
|
||||||
|
$ cd $WILDFLY_HOME
|
||||||
|
$ unzip keycloak-wf8-adapter-dist-{{book.project.version}}.zip
|
||||||
|
----
|
||||||
|
|
||||||
|
Install on JBoss EAP 7:
|
||||||
|
|
||||||
|
[source, subs="attributes"]
|
||||||
|
----
|
||||||
|
$ cd $EAP_HOME
|
||||||
|
$ unzip keycloak-eap7-adapter-dist-{{book.project.version}}.zip
|
||||||
|
----
|
||||||
|
|
||||||
|
Install on JBoss EAP 6:
|
||||||
|
|
||||||
|
[source, subs="attributes"]
|
||||||
|
----
|
||||||
|
$ cd $EAP_HOME
|
||||||
|
$ unzip keycloak-eap6-adapter-dist-{{book.project.version}}.zip
|
||||||
|
----
|
||||||
|
|
||||||
|
Install on JBoss AS 7.1:
|
||||||
|
|
||||||
|
[source, subs="attributes"]
|
||||||
|
----
|
||||||
|
$ cd $JBOSS_HOME
|
||||||
|
$ unzip keycloak-as7-adapter-dist-{{book.project.version}}.zip
|
||||||
|
----
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if book.product %}
|
||||||
|
Install on JBoss EAP 7:
|
||||||
|
|
||||||
|
[source, subs="attributes"]
|
||||||
|
----
|
||||||
|
$ cd $EAP_HOME
|
||||||
|
$ unzip rh-sso-{{book.project.version}}-eap7-adapter.zip
|
||||||
|
----
|
||||||
|
|
||||||
|
Install on JBoss EAP 6:
|
||||||
|
|
||||||
|
[source, subs="attributes"]
|
||||||
|
----
|
||||||
|
$ cd $EAP_HOME
|
||||||
|
$ unzip rh-sso-{{book.project.version}}-eap6-adapter.zip
|
||||||
|
----
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
This ZIP archive contains JBoss Modules specific to the {{book.project.name}} adapter. It also contains JBoss CLI scripts
|
||||||
|
to configure the adapter subsystem.
|
||||||
|
|
||||||
|
To configure the adapter subsystem if the server is not running execute:
|
||||||
|
|
||||||
|
[source]
|
||||||
|
----
|
||||||
|
$ ./bin/jboss-cli.sh --file=adapter-install-offline.cli
|
||||||
|
----
|
||||||
|
|
||||||
|
NOTE: The offline script is not available for JBoss EAP 6
|
||||||
|
|
||||||
|
Alternatively, if the server is running execute:
|
||||||
|
|
||||||
|
[source]
|
||||||
|
----
|
||||||
|
$ ./bin/jboss-cli.sh --file=adapter-install.cli
|
||||||
|
----
|
||||||
|
|
||||||
|
===== Required Per WAR Configuration
|
||||||
|
|
||||||
|
This section describes how to secure a WAR directly by adding configuration and editing files within your WAR package.
|
||||||
|
|
||||||
|
The first thing you must do is create a `keycloak.json` adapter configuration file within the `WEB-INF` directory of your WAR.
|
||||||
|
|
||||||
|
The format of this configuration file is described in the <<fake/../java-adapter-config.adoc#_java_adapter_config,Java adapter configuration>> section.
|
||||||
|
|
||||||
|
Next you must set the `auth-method` to `KEYCLOAK` in `web.xml`.
|
||||||
|
You also have to use standard servlet security to specify role-base constraints on your URLs.
|
||||||
|
|
||||||
|
Here's an example:
|
||||||
|
|
||||||
|
[source,xml]
|
||||||
|
----
|
||||||
|
|
||||||
|
<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">
|
||||||
|
|
||||||
|
<module-name>application</module-name>
|
||||||
|
|
||||||
|
<security-constraint>
|
||||||
|
<web-resource-collection>
|
||||||
|
<web-resource-name>Admins</web-resource-name>
|
||||||
|
<url-pattern>/admin/*</url-pattern>
|
||||||
|
</web-resource-collection>
|
||||||
|
<auth-constraint>
|
||||||
|
<role-name>admin</role-name>
|
||||||
|
</auth-constraint>
|
||||||
|
<user-data-constraint>
|
||||||
|
<transport-guarantee>CONFIDENTIAL</transport-guarantee>
|
||||||
|
</user-data-constraint>
|
||||||
|
</security-constraint>
|
||||||
|
<security-constraint>
|
||||||
|
<web-resource-collection>
|
||||||
|
<web-resource-name>Customers</web-resource-name>
|
||||||
|
<url-pattern>/customers/*</url-pattern>
|
||||||
|
</web-resource-collection>
|
||||||
|
<auth-constraint>
|
||||||
|
<role-name>user</role-name>
|
||||||
|
</auth-constraint>
|
||||||
|
<user-data-constraint>
|
||||||
|
<transport-guarantee>CONFIDENTIAL</transport-guarantee>
|
||||||
|
</user-data-constraint>
|
||||||
|
</security-constraint>
|
||||||
|
|
||||||
|
<login-config>
|
||||||
|
<auth-method>KEYCLOAK</auth-method>
|
||||||
|
<realm-name>this is ignored currently</realm-name>
|
||||||
|
</login-config>
|
||||||
|
|
||||||
|
<security-role>
|
||||||
|
<role-name>admin</role-name>
|
||||||
|
</security-role>
|
||||||
|
<security-role>
|
||||||
|
<role-name>user</role-name>
|
||||||
|
</security-role>
|
||||||
|
</web-app>
|
||||||
|
----
|
||||||
|
|
||||||
|
===== Securing WARs via Adapter Subsystem
|
||||||
|
|
||||||
|
You do not have to modify your WAR to secure it with {{book.project.name}}. Instead you can externally secure it via the {{book.project.name}} Adapter Subsystem.
|
||||||
|
While you don't have to specify KEYCLOAK as an `auth-method`, you still have to define the `security-constraints` in `web.xml`.
|
||||||
|
You do not, however, have to create a `WEB-INF/keycloak.json` file.
|
||||||
|
This metadata is instead defined within server configuration (i.e. `standalone.xml`) in the {{book.project.name}} subsystem definition.
|
||||||
|
|
||||||
|
[source,xml]
|
||||||
|
----
|
||||||
|
<extensions>
|
||||||
|
<extension module="org.keycloak.keycloak-adapter-subsystem"/>
|
||||||
|
</extensions>
|
||||||
|
|
||||||
|
<profile>
|
||||||
|
<subsystem xmlns="urn:jboss:domain:keycloak:1.1">
|
||||||
|
<secure-deployment name="WAR MODULE NAME.war">
|
||||||
|
<realm>demo</realm>
|
||||||
|
<auth-server-url>http://localhost:8081/auth</auth-server-url>
|
||||||
|
<ssl-required>external</ssl-required>
|
||||||
|
<resource>customer-portal</resource>
|
||||||
|
<credential name="secret">password</credential>
|
||||||
|
</secure-deployment>
|
||||||
|
</subsystem>
|
||||||
|
</profile>
|
||||||
|
----
|
||||||
|
|
||||||
|
The `secure-deployment` `name` attribute identifies the WAR you want to secure.
|
||||||
|
Its value is the `module-name` defined in `web.xml` with `.war` appended. The rest of the configuration corresponds pretty much one to one with the `keycloak.json` configuration options defined in <<fake/../java-adapter-config.adoc#_java_adapter_config,Java adapter configuration>>.
|
||||||
|
|
||||||
|
The exception is the `credential` element.
|
||||||
|
|
||||||
|
To make it easier for you, you can go to the {{book.project.name}} Administration Console and go to the Client/Installation tab of the application this WAR is aligned with.
|
||||||
|
It provides an example XML file you can cut and paste.
|
||||||
|
|
||||||
|
If you have multiple deployments secured by the same realm you can share the realm configuration in a separate element. For example:
|
||||||
|
|
||||||
|
[source,xml]
|
||||||
|
----
|
||||||
|
<subsystem xmlns="urn:jboss:domain:keycloak:1.1">
|
||||||
|
<realm name="demo">
|
||||||
|
<auth-server-url>http://localhost:8080/auth</auth-server-url>
|
||||||
|
<ssl-required>external</ssl-required>
|
||||||
|
</realm>
|
||||||
|
<secure-deployment name="customer-portal.war">
|
||||||
|
<realm>demo</realm>
|
||||||
|
<resource>customer-portal</resource>
|
||||||
|
<credential name="secret">password</credential>
|
||||||
|
</secure-deployment>
|
||||||
|
<secure-deployment name="product-portal.war">
|
||||||
|
<realm>demo</realm>
|
||||||
|
<resource>product-portal</resource>
|
||||||
|
<credential name="secret">password</credential>
|
||||||
|
</secure-deployment>
|
||||||
|
<secure-deployment name="database.war">
|
||||||
|
<realm>demo</realm>
|
||||||
|
<resource>database-service</resource>
|
||||||
|
<bearer-only>true</bearer-only>
|
||||||
|
</secure-deployment>
|
||||||
|
</subsystem>
|
||||||
|
----
|
||||||
|
|
||||||
|
|
||||||
|
===== Security Domain
|
||||||
|
|
||||||
|
To propagate the security context to the EJB tier you need to configure it to use the "keycloak" security domain. This
|
||||||
|
can be achieved with the @SecurityDomain annotation:
|
||||||
|
|
||||||
|
[source]
|
||||||
|
----
|
||||||
|
|
||||||
|
import org.jboss.ejb3.annotation.SecurityDomain;
|
||||||
|
...
|
||||||
|
|
||||||
|
@Stateless
|
||||||
|
@SecurityDomain("keycloak")
|
||||||
|
public class CustomerService {
|
||||||
|
|
||||||
|
@RolesAllowed("user")
|
||||||
|
public List<String> getCustomers() {
|
||||||
|
return db.getCustomers();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
----
|
47
securing_apps/topics/oidc/java/jetty8-adapter.adoc
Executable file
47
securing_apps/topics/oidc/java/jetty8-adapter.adoc
Executable file
|
@ -0,0 +1,47 @@
|
||||||
|
|
||||||
|
[[_jetty8_adapter]]
|
||||||
|
==== Jetty 8.1.x Adapter
|
||||||
|
|
||||||
|
Keycloak has a separate adapter for Jetty 8.1.x that you will have to install into your Jetty installation.
|
||||||
|
You then have to provide some extra configuration in each WAR you deploy to Jetty.
|
||||||
|
Let's go over these steps.
|
||||||
|
|
||||||
|
[[_jetty8_adapter_installation]]
|
||||||
|
===== Adapter Installation
|
||||||
|
|
||||||
|
Adapters are no longer included with the appliance or war distribution.Each adapter is a separate download on the Keycloak download site.
|
||||||
|
They are also available as a maven artifact.
|
||||||
|
|
||||||
|
You must unzip the Jetty 8.1.x distro into Jetty 8.1.x's root directory.
|
||||||
|
Including adapter's jars within your WEB-INF/lib directory will not work!
|
||||||
|
|
||||||
|
|
||||||
|
[source]
|
||||||
|
----
|
||||||
|
|
||||||
|
$ cd $JETTY_HOME
|
||||||
|
$ unzip keycloak-jetty81-adapter-dist.zip
|
||||||
|
----
|
||||||
|
|
||||||
|
Next, you will have to enable the keycloak option.
|
||||||
|
Edit start.ini and add keycloak to the options
|
||||||
|
|
||||||
|
|
||||||
|
[source]
|
||||||
|
----
|
||||||
|
|
||||||
|
|
||||||
|
#===========================================================
|
||||||
|
# Start classpath OPTIONS.
|
||||||
|
# These control what classes are on the classpath
|
||||||
|
# for a full listing do
|
||||||
|
# java -jar start.jar --list-options
|
||||||
|
#-----------------------------------------------------------
|
||||||
|
OPTIONS=Server,jsp,jmx,resources,websocket,ext,plus,annotations,keycloak
|
||||||
|
----
|
||||||
|
|
||||||
|
===== Required Per WAR Configuration
|
||||||
|
|
||||||
|
Enabling Keycloak for your WARs is the same as the Jetty 9.x adapter.
|
||||||
|
Our 8.1.x adapter supports both keycloak.json and the jboss-web.xml advanced configuration.
|
||||||
|
See <<fake/../jetty9-adapter.adoc#_jetty9_per_war,Required Per WAR Configuration>>
|
146
securing_apps/topics/oidc/java/jetty9-adapter.adoc
Executable file
146
securing_apps/topics/oidc/java/jetty9-adapter.adoc
Executable file
|
@ -0,0 +1,146 @@
|
||||||
|
|
||||||
|
[[_jetty9_adapter]]
|
||||||
|
==== Jetty 9.x Adapters
|
||||||
|
|
||||||
|
Keycloak has a separate adapter for Jetty 9.1.x, Jetty 9.2.x and Jetty 9.3.x that you will have to install into your Jetty installation.
|
||||||
|
You then have to provide some extra configuration in each WAR you deploy to Jetty.
|
||||||
|
Let's go over these steps.
|
||||||
|
|
||||||
|
[[_jetty9_adapter_installation]]
|
||||||
|
===== Adapter Installation
|
||||||
|
|
||||||
|
Adapters are no longer included with the appliance or war distribution.Each adapter is a separate download on the Keycloak download site.
|
||||||
|
They are also available as a maven artifact.
|
||||||
|
|
||||||
|
You must unzip the Jetty 9.x distro into Jetty 9.x's link:https://www.eclipse.org/jetty/documentation/current/startup-base-and-home.html[base directory.]
|
||||||
|
Including adapter's jars within your WEB-INF/lib directory will not work!
|
||||||
|
In the example below, the Jetty base is named `your-base`:
|
||||||
|
|
||||||
|
[source]
|
||||||
|
----
|
||||||
|
|
||||||
|
$ cd your-base
|
||||||
|
$ unzip keycloak-jetty93-adapter-dist-2.5.0.Final.zip
|
||||||
|
----
|
||||||
|
|
||||||
|
Next, you will have to enable the `keycloak` module for your Jetty base:
|
||||||
|
|
||||||
|
[source]
|
||||||
|
----
|
||||||
|
|
||||||
|
$ java -jar $JETTY_HOME/start.jar --add-to-startd=keycloak
|
||||||
|
----
|
||||||
|
|
||||||
|
[[_jetty9_per_war]]
|
||||||
|
===== Required Per WAR Configuration
|
||||||
|
|
||||||
|
This section describes how to secure a WAR directly by adding config and editing files within your WAR package.
|
||||||
|
|
||||||
|
The first thing you must do is create a `WEB-INF/jetty-web.xml` file in your WAR package.
|
||||||
|
This is a Jetty specific config file and you must define a Keycloak specific authenticator within it.
|
||||||
|
|
||||||
|
[source]
|
||||||
|
----
|
||||||
|
|
||||||
|
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
|
||||||
|
<Configure class="org.eclipse.jetty.webapp.WebAppContext">
|
||||||
|
<Get name="securityHandler">
|
||||||
|
<Set name="authenticator">
|
||||||
|
<New class="org.keycloak.adapters.jetty.KeycloakJettyAuthenticator">
|
||||||
|
</New>
|
||||||
|
</Set>
|
||||||
|
</Get>
|
||||||
|
</Configure>
|
||||||
|
----
|
||||||
|
|
||||||
|
Next you must create a `keycloak.json` adapter config file within the `WEB-INF` directory of your WAR.
|
||||||
|
|
||||||
|
The format of this config file is describe in the <<fake/../java-adapter-config.adoc#_java_adapter_config,Java adapter configuration>> section.
|
||||||
|
|
||||||
|
WARNING: The Jetty 9.1.x adapter will not be able to find the `keycloak.json` file.
|
||||||
|
You will have to define all adapter settings within the `jetty-web.xml` file as described below.
|
||||||
|
|
||||||
|
Instead of using keycloak.json, you can define everything within the `jetty-web.xml`.
|
||||||
|
You'll just have to figure out how the json settings match to the `org.keycloak.representations.adapters.config.AdapterConfig` class.
|
||||||
|
|
||||||
|
|
||||||
|
[source]
|
||||||
|
----
|
||||||
|
|
||||||
|
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
|
||||||
|
<Configure class="org.eclipse.jetty.webapp.WebAppContext">
|
||||||
|
<Get name="securityHandler">
|
||||||
|
<Set name="authenticator">
|
||||||
|
<New class="org.keycloak.adapters.jetty.KeycloakJettyAuthenticator">
|
||||||
|
<Set name="adapterConfig">
|
||||||
|
<New class="org.keycloak.representations.adapters.config.AdapterConfig">
|
||||||
|
<Set name="realm">tomcat</Set>
|
||||||
|
<Set name="resource">customer-portal</Set>
|
||||||
|
<Set name="authServerUrl">http://localhost:8081/auth</Set>
|
||||||
|
<Set name="sslRequired">external</Set>
|
||||||
|
<Set name="credentials">
|
||||||
|
<Map>
|
||||||
|
<Entry>
|
||||||
|
<Item>secret</Item>
|
||||||
|
<Item>password</Item>
|
||||||
|
</Entry>
|
||||||
|
</Map>
|
||||||
|
</Set>
|
||||||
|
</New>
|
||||||
|
</Set>
|
||||||
|
</New>
|
||||||
|
</Set>
|
||||||
|
</Get>
|
||||||
|
</Configure>
|
||||||
|
----
|
||||||
|
|
||||||
|
You do not have to crack open your WAR to secure it with keycloak.
|
||||||
|
Instead create the jetty-web.xml file in your webapps directory with the name of yourwar.xml.
|
||||||
|
Jetty should pick it up.
|
||||||
|
In this mode, you'll have to declare keycloak.json configuration directly within the xml file.
|
||||||
|
|
||||||
|
Finally you must specify both a `login-config` and use standard servlet security to specify role-base constraints on your URLs.
|
||||||
|
Here's an example:
|
||||||
|
|
||||||
|
|
||||||
|
[source]
|
||||||
|
----
|
||||||
|
|
||||||
|
|
||||||
|
<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">
|
||||||
|
|
||||||
|
<module-name>customer-portal</module-name>
|
||||||
|
|
||||||
|
<security-constraint>
|
||||||
|
<web-resource-collection>
|
||||||
|
<web-resource-name>Customers</web-resource-name>
|
||||||
|
<url-pattern>/*</url-pattern>
|
||||||
|
</web-resource-collection>
|
||||||
|
<auth-constraint>
|
||||||
|
<role-name>user</role-name>
|
||||||
|
</auth-constraint>
|
||||||
|
<user-data-constraint>
|
||||||
|
<transport-guarantee>CONFIDENTIAL</transport-guarantee>
|
||||||
|
</user-data-constraint>
|
||||||
|
</security-constraint>
|
||||||
|
|
||||||
|
<login-config>
|
||||||
|
<auth-method>BASIC</auth-method>
|
||||||
|
<realm-name>this is ignored currently</realm-name>
|
||||||
|
</login-config>
|
||||||
|
|
||||||
|
<security-role>
|
||||||
|
<role-name>admin</role-name>
|
||||||
|
</security-role>
|
||||||
|
<security-role>
|
||||||
|
<role-name>user</role-name>
|
||||||
|
</security-role>
|
||||||
|
</web-app>
|
||||||
|
----
|
6
securing_apps/topics/oidc/java/logout.adoc
Executable file
6
securing_apps/topics/oidc/java/logout.adoc
Executable file
|
@ -0,0 +1,6 @@
|
||||||
|
==== Logout
|
||||||
|
|
||||||
|
You can log out of a web application in multiple ways.
|
||||||
|
For Java EE servlet containers, you can call HttpServletRequest.logout(). For other browser applications, you can redirect the browser to
|
||||||
|
`$$http://auth-server/auth/realms/{realm-name}/protocol/openid-connect/logout?redirect_uri=encodedRedirectUri$$`, which logs you out if you have an SSO session with your browser.
|
||||||
|
|
54
securing_apps/topics/oidc/java/multi-tenancy.adoc
Executable file
54
securing_apps/topics/oidc/java/multi-tenancy.adoc
Executable file
|
@ -0,0 +1,54 @@
|
||||||
|
|
||||||
|
==== Multi Tenancy
|
||||||
|
|
||||||
|
Multi Tenancy, in our context, means that a single target application (WAR) can be secured with multiple {{book.project.name}} realms. The realms can be located
|
||||||
|
one the same {{book.project.name}} instance or on different instances.
|
||||||
|
|
||||||
|
In practice, this means that the application needs to have multiple `keycloak.json` adapter configuration files.
|
||||||
|
|
||||||
|
You could have multiple instances of your WAR with different adapter configuration files deployed to different context-paths. However, this may be inconvenient
|
||||||
|
and you may also want to select the realm based on something else than context-path.
|
||||||
|
|
||||||
|
{{book.project.name}} makes it possible to have a custom config resolver so you can choose what adapter config is used for each request.
|
||||||
|
|
||||||
|
To achieve this first you need to create an implementation of `org.keycloak.adapters.KeycloakConfigResolver`. For example:
|
||||||
|
|
||||||
|
[source,java]
|
||||||
|
----
|
||||||
|
package example;
|
||||||
|
|
||||||
|
import org.keycloak.adapters.KeycloakConfigResolver;
|
||||||
|
import org.keycloak.adapters.KeycloakDeployment;
|
||||||
|
import org.keycloak.adapters.KeycloakDeploymentBuilder;
|
||||||
|
|
||||||
|
public class PathBasedKeycloakConfigResolver implements KeycloakConfigResolver {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public KeycloakDeployment resolve(OIDCHttpFacade.Request request) {
|
||||||
|
if (path.startsWith("alternative")) {
|
||||||
|
KeycloakDeployment deployment = cache.get(realm);
|
||||||
|
if (null == deployment) {
|
||||||
|
InputStream is = getClass().getResourceAsStream("/tenant1-keycloak.json");
|
||||||
|
return KeycloakDeploymentBuilder.build(is);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
InputStream is = getClass().getResourceAsStream("/default-keycloak.json");
|
||||||
|
return KeycloakDeploymentBuilder.build(is);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
|
You also need to configure which `KeycloakConfigResolver` implementation to use with the `keycloak.config.resolver` context-param in your `web.xml`:
|
||||||
|
|
||||||
|
[source,xml]
|
||||||
|
----
|
||||||
|
<web-app>
|
||||||
|
...
|
||||||
|
<context-param>
|
||||||
|
<param-name>keycloak.config.resolver</param-name>
|
||||||
|
<param-value>example.PathBasedKeycloakConfigResolver</param-value>
|
||||||
|
</context-param>
|
||||||
|
</web-app>
|
||||||
|
----
|
32
securing_apps/topics/oidc/java/params_forwarding.adoc
Normal file
32
securing_apps/topics/oidc/java/params_forwarding.adoc
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
|
||||||
|
==== Parameters Forwarding
|
||||||
|
|
||||||
|
The {{book.project.name}} initial authorization endpoint request has support for various parameters. Most of the parameters are described in
|
||||||
|
http://openid.net/specs/openid-connect-core-1_0.html#AuthorizationEndpoint[OIDC specification]. Some parameters are added automatically by the adapter based
|
||||||
|
on the adapter configuration. However, there are also a few parameters that can be added on a per-invocation basis. When you open the secured application URI,
|
||||||
|
the particular parameter will be forwarded to the {{book.project.name}} authorization endpoint.
|
||||||
|
|
||||||
|
For example, if you request an offline token, then you can open the secured application URI with the `scope` parameter like:
|
||||||
|
|
||||||
|
[source]
|
||||||
|
----
|
||||||
|
http://myappserver/mysecuredapp?scope=offline_access
|
||||||
|
----
|
||||||
|
|
||||||
|
and the parameter `scope=offline_access` will be automatically forwarded to the {{book.project.name}} authorization endpoint.
|
||||||
|
|
||||||
|
The supported parameters are:
|
||||||
|
|
||||||
|
* scope
|
||||||
|
|
||||||
|
* prompt
|
||||||
|
|
||||||
|
* max_age
|
||||||
|
|
||||||
|
* login_hint
|
||||||
|
|
||||||
|
* kc_idp_hint
|
||||||
|
|
||||||
|
Most of the parameters are described in the http://openid.net/specs/openid-connect-core-1_0.html#AuthorizationEndpoint[OIDC specification].
|
||||||
|
The only exception is parameter `kc_idp_hint`, which is specific to {{book.project.name}} and contains the name of the identity provider to automatically use.
|
||||||
|
For more information see the `Identity Brokering` section in {{book.project.doc_base_url}}{{book.project.doc_info_version_url}}{{book.adminguide.link}}[{{book.adminguide.name}}].
|
66
securing_apps/topics/oidc/java/servlet-filter-adapter.adoc
Normal file
66
securing_apps/topics/oidc/java/servlet-filter-adapter.adoc
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
[[_servlet_filter_adapter]]
|
||||||
|
==== Java Servlet Filter Adapter
|
||||||
|
|
||||||
|
If you are deploying your Java Servlet application on a platform where there is no {{book.project.name}} adapter you opt to use the servlet filter adapter.
|
||||||
|
This adapter works a bit differently than the other adapters. You do not define security constraints in web.xml.
|
||||||
|
Instead you define a filter mapping using the {{book.project.name}} servlet filter adapter to secure the url patterns you want to secure.
|
||||||
|
|
||||||
|
WARNING: Backchannel logout works a bit differently than the standard adapters.
|
||||||
|
Instead of invalidating the HTTP session it marks the session id as logged out.
|
||||||
|
There's no way standard way to invalidate an HTTP session based on a session id.
|
||||||
|
|
||||||
|
[source,xml]
|
||||||
|
----
|
||||||
|
<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">
|
||||||
|
|
||||||
|
<module-name>application</module-name>
|
||||||
|
|
||||||
|
<filter>
|
||||||
|
<filter-name>Keycloak Filter</filter-name>
|
||||||
|
<filter-class>org.keycloak.adapters.servlet.KeycloakOIDCFilter</filter-class>
|
||||||
|
</filter>
|
||||||
|
<filter-mapping>
|
||||||
|
<filter-name>Keycloak Filter</filter-name>
|
||||||
|
<url-pattern>/keycloak/*</url-pattern>
|
||||||
|
<url-pattern>/protected/*</url-pattern>
|
||||||
|
</filter-mapping>
|
||||||
|
</web-app>
|
||||||
|
----
|
||||||
|
|
||||||
|
In the snippet above there are two url-patterns.
|
||||||
|
_/protected/*_ are the files we want protected, while the _/keycloak/*_ url-pattern handles callbacks from the {{book.project.name}} server.
|
||||||
|
|
||||||
|
If you need to exclude some paths beneath the configured `url-patterns` you can use the Filter init-param `keycloak.config.skipPattern` to configure
|
||||||
|
a regular expression that describes a path-pattern for which the keycloak filter should immediately delegate to the filter-chain.
|
||||||
|
By default no skipPattern is configured.
|
||||||
|
|
||||||
|
Patterns are matched against the `requestURI` without the `context-path`. Given the context-path `/myapp` a request for `/myapp/index.html` will be matched with `/index.html` against the skip pattern.
|
||||||
|
|
||||||
|
[source,xml]
|
||||||
|
----
|
||||||
|
<init-param>
|
||||||
|
<param-name>keycloak.config.skipPattern</param-name>
|
||||||
|
<param-value>^/(path1|path2|path3).*</param-value>
|
||||||
|
</init-param>
|
||||||
|
----
|
||||||
|
|
||||||
|
Note that you should configure your client in the {{book.project.name}} Admin Console with an Admin URL that points to a secured section covered by the filter's url-pattern.
|
||||||
|
|
||||||
|
The Admin URL will make callbacks to the Admin URL to do things like backchannel logout.
|
||||||
|
So, the Admin URL in this example should be `http[s]://hostname/{context-root}/keycloak`.
|
||||||
|
|
||||||
|
The {{book.project.name}} filter has the same configuration parameters as the other adapters except you must define them as filter init params instead of context params.
|
||||||
|
|
||||||
|
To use this filter, include this maven artifact in your WAR poms:
|
||||||
|
|
||||||
|
[source,xml,subs="attributes+"]
|
||||||
|
----
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.keycloak</groupId>
|
||||||
|
<artifactId>keycloak-servlet-filter-adapter</artifactId>
|
||||||
|
<version>{{book.project.versionMvn}}</version>
|
||||||
|
</dependency>
|
||||||
|
----
|
75
securing_apps/topics/oidc/java/spring-boot-adapter.adoc
Executable file
75
securing_apps/topics/oidc/java/spring-boot-adapter.adoc
Executable file
|
@ -0,0 +1,75 @@
|
||||||
|
[[_spring_boot_adapter]]
|
||||||
|
==== Spring Boot Adapter
|
||||||
|
|
||||||
|
To be able to secure Spring Boot apps you must add the Keycloak Spring Boot adapter JAR to your app.
|
||||||
|
You then have to provide some extra configuration via normal Spring Boot configuration (`application.properties`). Let's go over these steps.
|
||||||
|
|
||||||
|
[[_spring_boot_adapter_installation]]
|
||||||
|
===== Adapter Installation
|
||||||
|
|
||||||
|
The Keycloak Spring Boot adapter takes advantage of Spring Boot's autoconfiguration so all you need to do is add the Keycloak Spring Boot adapter JAR to your project.
|
||||||
|
Depending on what container you are using with Spring Boot, you also need to add the appropriate Keycloak container adapter.
|
||||||
|
If you are using Maven, add the following to your pom.xml (using Tomcat as an example):
|
||||||
|
|
||||||
|
|
||||||
|
[source,xml,subs="attributes+"]
|
||||||
|
----
|
||||||
|
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.keycloak</groupId>
|
||||||
|
<artifactId>keycloak-spring-boot-adapter</artifactId>
|
||||||
|
<version>{{book.project.versionMvn}}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.keycloak</groupId>
|
||||||
|
<artifactId>keycloak-tomcat8-adapter</artifactId>
|
||||||
|
<version>{{book.project.versionMvn}}</version>
|
||||||
|
</dependency>
|
||||||
|
----
|
||||||
|
|
||||||
|
Currently the following embedded containers are supported :
|
||||||
|
|
||||||
|
* Tomcat
|
||||||
|
* Undertow
|
||||||
|
* Jetty
|
||||||
|
|
||||||
|
[[_spring_boot_adapter_configuration]]
|
||||||
|
===== Required Spring Boot Adapter Configuration
|
||||||
|
|
||||||
|
This section describes how to configure your Spring Boot app to use Keycloak.
|
||||||
|
|
||||||
|
Instead of a `keycloak.json` file, you configure the realm for the Spring Boot Keycloak adapter via the normal Spring Boot configuration.
|
||||||
|
For example:
|
||||||
|
|
||||||
|
[source]
|
||||||
|
----
|
||||||
|
|
||||||
|
|
||||||
|
keycloak.realm = demorealm
|
||||||
|
keycloak.auth-server-url = http://127.0.0.1:8080/auth
|
||||||
|
keycloak.ssl-required = external
|
||||||
|
keycloak.resource = demoapp
|
||||||
|
keycloak.credentials.secret = 11111111-1111-1111-1111-111111111111
|
||||||
|
keycloak.use-resource-role-mappings = true
|
||||||
|
----
|
||||||
|
|
||||||
|
To configure a Policy Enforcer, unlike keycloak.json, `policy-enforcer-config` must be used instead of just `policy-enforcer`.
|
||||||
|
|
||||||
|
You also need to specify the Java EE security config that would normally go in the `web.xml`.
|
||||||
|
The Spring Boot Adapter will set the `login-method` to `KEYCLOAK` and configure the `security-constraints` at startup time.
|
||||||
|
Here's an example configuration:
|
||||||
|
|
||||||
|
[source]
|
||||||
|
----
|
||||||
|
|
||||||
|
|
||||||
|
keycloak.securityConstraints[0].securityCollections[0].name = insecure stuff
|
||||||
|
keycloak.securityConstraints[0].securityCollections[0].authRoles[0] = admin
|
||||||
|
keycloak.securityConstraints[0].securityCollections[0].authRoles[1] = user
|
||||||
|
keycloak.securityConstraints[0].securityCollections[0].patterns[0] = /insecure
|
||||||
|
|
||||||
|
keycloak.securityConstraints[0].securityCollections[1].name = admin stuff
|
||||||
|
keycloak.securityConstraints[0].securityCollections[1].authRoles[0] = admin
|
||||||
|
keycloak.securityConstraints[0].securityCollections[1].patterns[0] = /admin
|
||||||
|
----
|
280
securing_apps/topics/oidc/java/spring-security-adapter.adoc
Executable file
280
securing_apps/topics/oidc/java/spring-security-adapter.adoc
Executable file
|
@ -0,0 +1,280 @@
|
||||||
|
[[_spring_security_adapter]]
|
||||||
|
==== Spring Security Adapter
|
||||||
|
|
||||||
|
To secure an application with Spring Security and Keycloak, add this adapter as a dependency to your project.
|
||||||
|
You then have to provide some extra beans in your Spring Security configuration file and add the Keycloak security filter to your pipeline.
|
||||||
|
|
||||||
|
Unlike the other Keycloak Adapters, you should not configure your security in web.xml.
|
||||||
|
However, keycloak.json is still required.
|
||||||
|
|
||||||
|
===== Adapter Installation
|
||||||
|
|
||||||
|
Add Keycloak Spring Security adapter as a dependency to your Maven POM or Gradle build.
|
||||||
|
|
||||||
|
|
||||||
|
[source,xml,subs="attributes+"]
|
||||||
|
----
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.keycloak</groupId>
|
||||||
|
<artifactId>keycloak-spring-security-adapter</artifactId>
|
||||||
|
<version>{{book.project.versionMvn}}</version>
|
||||||
|
</dependency>
|
||||||
|
----
|
||||||
|
|
||||||
|
===== Spring Security Configuration
|
||||||
|
|
||||||
|
The Keycloak Spring Security adapter takes advantage of Spring Security's flexible security configuration syntax.
|
||||||
|
|
||||||
|
====== Java Configuration
|
||||||
|
|
||||||
|
Keycloak provides a KeycloakWebSecurityConfigurerAdapter as a convenient base class for creating a http://docs.spring.io/spring-security/site/docs/4.0.x/apidocs/org/springframework/security/config/annotation/web/WebSecurityConfigurer.html[WebSecurityConfigurer] instance.
|
||||||
|
The implementation allows customization by overriding methods.
|
||||||
|
While its use is not required, it greatly simplifies your security context configuration.
|
||||||
|
|
||||||
|
|
||||||
|
[source]
|
||||||
|
----
|
||||||
|
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableWebSecurity
|
||||||
|
@ComponentScan(basePackageClasses = KeycloakSecurityComponents.class)
|
||||||
|
public class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Registers the KeycloakAuthenticationProvider with the authentication manager.
|
||||||
|
*/
|
||||||
|
@Autowired
|
||||||
|
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
|
||||||
|
auth.authenticationProvider(keycloakAuthenticationProvider());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines the session authentication strategy.
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
@Override
|
||||||
|
protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
|
||||||
|
return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void configure(HttpSecurity http) throws Exception
|
||||||
|
{
|
||||||
|
super.configure(http);
|
||||||
|
http
|
||||||
|
.authorizeRequests()
|
||||||
|
.antMatchers("/customers*").hasRole("USER")
|
||||||
|
.antMatchers("/admin*").hasRole("ADMIN")
|
||||||
|
.anyRequest().permitAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
|
You must provide a session authentication strategy bean which should be of type `RegisterSessionAuthenticationStrategy` for public or confidential applications and `NullAuthenticatedSessionStrategy` for bearer-only applications.
|
||||||
|
|
||||||
|
Spring Security's `SessionFixationProtectionStrategy` is currently not supported because it changes the session identifier after login via Keycloak.
|
||||||
|
If the session identifier changes, universal log out will not work because Keycloak is unaware of the new session identifier.
|
||||||
|
|
||||||
|
====== XML Configuration
|
||||||
|
|
||||||
|
While Spring Security's XML namespace simplifies configuration, customizing the configuration can be a bit verbose.
|
||||||
|
|
||||||
|
|
||||||
|
[source]
|
||||||
|
----
|
||||||
|
|
||||||
|
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||||
|
xmlns:context="http://www.springframework.org/schema/context"
|
||||||
|
xmlns:security="http://www.springframework.org/schema/security"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="
|
||||||
|
http://www.springframework.org/schema/beans
|
||||||
|
http://www.springframework.org/schema/beans/spring-beans.xsd
|
||||||
|
http://www.springframework.org/schema/context
|
||||||
|
http://www.springframework.org/schema/context/spring-context.xsd
|
||||||
|
http://www.springframework.org/schema/security
|
||||||
|
http://www.springframework.org/schema/security/spring-security.xsd">
|
||||||
|
|
||||||
|
<context:component-scan base-package="org.keycloak.adapters.springsecurity" />
|
||||||
|
|
||||||
|
<security:authentication-manager alias="authenticationManager">
|
||||||
|
<security:authentication-provider ref="keycloakAuthenticationProvider" />
|
||||||
|
</security:authentication-manager>
|
||||||
|
|
||||||
|
<bean id="adapterDeploymentContext" class="org.keycloak.adapters.springsecurity.AdapterDeploymentContextFactoryBean">
|
||||||
|
<constructor-arg value="/WEB-INF/keycloak.json" />
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<bean id="keycloakAuthenticationEntryPoint" class="org.keycloak.adapters.springsecurity.authentication.KeycloakAuthenticationEntryPoint" />
|
||||||
|
<bean id="keycloakAuthenticationProvider" class="org.keycloak.adapters.springsecurity.authentication.KeycloakAuthenticationProvider" />
|
||||||
|
<bean id="keycloakPreAuthActionsFilter" class="org.keycloak.adapters.springsecurity.filter.KeycloakPreAuthActionsFilter" />
|
||||||
|
<bean id="keycloakAuthenticationProcessingFilter" class="org.keycloak.adapters.springsecurity.filter.KeycloakAuthenticationProcessingFilter">
|
||||||
|
<constructor-arg name="authenticationManager" ref="authenticationManager" />
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<bean id="keycloakLogoutHandler" class="org.keycloak.adapters.springsecurity.authentication.KeycloakLogoutHandler">
|
||||||
|
<constructor-arg ref="adapterDeploymentContext" />
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<bean id="logoutFilter" class="org.springframework.security.web.authentication.logout.LogoutFilter">
|
||||||
|
<constructor-arg name="logoutSuccessUrl" value="/" />
|
||||||
|
<constructor-arg name="handlers">
|
||||||
|
<list>
|
||||||
|
<ref bean="keycloakLogoutHandler" />
|
||||||
|
<bean class="org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler" />
|
||||||
|
</list>
|
||||||
|
</constructor-arg>
|
||||||
|
<property name="logoutRequestMatcher">
|
||||||
|
<bean class="org.springframework.security.web.util.matcher.AntPathRequestMatcher">
|
||||||
|
<constructor-arg name="pattern" value="/sso/logout**" />
|
||||||
|
<constructor-arg name="httpMethod" value="GET" />
|
||||||
|
</bean>
|
||||||
|
</property>
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<security:http auto-config="false" entry-point-ref="keycloakAuthenticationEntryPoint">
|
||||||
|
<security:custom-filter ref="keycloakPreAuthActionsFilter" before="LOGOUT_FILTER" />
|
||||||
|
<security:custom-filter ref="keycloakAuthenticationProcessingFilter" before="FORM_LOGIN_FILTER" />
|
||||||
|
<security:intercept-url pattern="/customers**" access="ROLE_USER" />
|
||||||
|
<security:intercept-url pattern="/admin**" access="ROLE_ADMIN" />
|
||||||
|
<security:custom-filter ref="logoutFilter" position="LOGOUT_FILTER" />
|
||||||
|
</security:http>
|
||||||
|
|
||||||
|
</beans>
|
||||||
|
----
|
||||||
|
|
||||||
|
===== Multi Tenancy
|
||||||
|
|
||||||
|
The Keycloak Spring Security adapter also supports multi tenancy.
|
||||||
|
Instead of injecting `AdapterDeploymentContextFactoryBean` with the path to `keycloak.json` you can inject an implementation of the `KeycloakConfigResolver` interface.
|
||||||
|
More details on how to implement the `KeycloakConfigResolver` can be found in <<fake/../multi-tenancy.adoc#_multi_tenancy,Multi Tenancy>>.
|
||||||
|
|
||||||
|
===== Naming Security Roles
|
||||||
|
|
||||||
|
Spring Security, when using role-based authentication, requires that role names start with `ROLE_`.
|
||||||
|
For example, an administrator role must be declared in Keycloak as `ROLE_ADMIN` or similar, not simply `ADMIN`.
|
||||||
|
|
||||||
|
The class `org.keycloak.adapters.springsecurity.authentication.KeycloakAuthenticationProvider` supports an optional `org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper` which can be used to map roles coming from Keycloak to roles recognized by Spring Security.
|
||||||
|
Use, for example, `org.springframework.security.core.authority.mapping.SimpleAuthorityMapper` to insert the `ROLE_` prefix and convert the role name to upper case.
|
||||||
|
The class is part of Spring Security Core module.
|
||||||
|
|
||||||
|
===== Client to Client Support
|
||||||
|
|
||||||
|
To simplify communication between clients, Keycloak provides an extension of Spring's `RestTemplate` that handles bearer token authentication for you.
|
||||||
|
To enable this feature your security configuration must add the `KeycloakRestTemplate` bean.
|
||||||
|
Note that it must be scoped as a prototype to function correctly.
|
||||||
|
|
||||||
|
For Java configuration:
|
||||||
|
[source]
|
||||||
|
----
|
||||||
|
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableWebSecurity
|
||||||
|
@ComponentScan(basePackageClasses = KeycloakSecurityComponents.class)
|
||||||
|
public class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter {
|
||||||
|
|
||||||
|
...
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
public KeycloakClientRequestFactory keycloakClientRequestFactory;
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
|
||||||
|
public KeycloakRestTemplate keycloakRestTemplate() {
|
||||||
|
return new KeycloakRestTemplate(keycloakClientRequestFactory);
|
||||||
|
}
|
||||||
|
|
||||||
|
...
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
|
For XML configuration:
|
||||||
|
[source]
|
||||||
|
----
|
||||||
|
|
||||||
|
|
||||||
|
<bean id="keycloakRestTemplate" class="org.keycloak.adapters.springsecurity.client.KeycloakRestTemplate" scope="prototype">
|
||||||
|
<constructor-arg name="factory" ref="keycloakClientRequestFactory" />
|
||||||
|
</bean>
|
||||||
|
----
|
||||||
|
|
||||||
|
Your application code can then use `KeycloakRestTemplate` any time it needs to make a call to another client.
|
||||||
|
For example:
|
||||||
|
[source]
|
||||||
|
----
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class RemoteProductService implements ProductService {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private KeycloakRestTemplate template;
|
||||||
|
|
||||||
|
private String endpoint;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> getProducts() {
|
||||||
|
ResponseEntity<String[]> response = template.getForEntity(endpoint, String[].class);
|
||||||
|
return Arrays.asList(response.getBody());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
|
===== Spring Boot Integration
|
||||||
|
|
||||||
|
The Spring Boot and the Spring Security adapters can be combined.
|
||||||
|
|
||||||
|
====== Using Spring Boot Configuration
|
||||||
|
|
||||||
|
By Default, the Spring Security Adapter looks for a `keycloak.json` configuration file. You can make sure it looks at the configuration provided by the Spring Boot Adapter by adding this bean :
|
||||||
|
|
||||||
|
[source]
|
||||||
|
----
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public KeycloakConfigResolver KeycloakConfigResolver() {
|
||||||
|
return new KeycloakSpringBootConfigResolver();
|
||||||
|
}
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
====== Avoid double Filter bean registration
|
||||||
|
|
||||||
|
Spring Boot attempts to eagerly register filter beans with the web application context.
|
||||||
|
Therefore, when running the Keycloak Spring Security adapter in a Spring Boot environment, it may be necessary to add two ``FilterRegistrationBean``s to your security configuration to prevent the Keycloak filters from being registered twice.
|
||||||
|
|
||||||
|
|
||||||
|
[source]
|
||||||
|
----
|
||||||
|
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableWebSecurity
|
||||||
|
public class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter
|
||||||
|
{
|
||||||
|
...
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public FilterRegistrationBean keycloakAuthenticationProcessingFilterRegistrationBean(
|
||||||
|
KeycloakAuthenticationProcessingFilter filter) {
|
||||||
|
FilterRegistrationBean registrationBean = new FilterRegistrationBean(filter);
|
||||||
|
registrationBean.setEnabled(false);
|
||||||
|
return registrationBean;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public FilterRegistrationBean keycloakPreAuthActionsFilterRegistrationBean(
|
||||||
|
KeycloakPreAuthActionsFilter filter) {
|
||||||
|
FilterRegistrationBean registrationBean = new FilterRegistrationBean(filter);
|
||||||
|
registrationBean.setEnabled(false);
|
||||||
|
return registrationBean;
|
||||||
|
}
|
||||||
|
|
||||||
|
...
|
||||||
|
}
|
||||||
|
----
|
88
securing_apps/topics/oidc/java/tomcat-adapter.adoc
Executable file
88
securing_apps/topics/oidc/java/tomcat-adapter.adoc
Executable file
|
@ -0,0 +1,88 @@
|
||||||
|
|
||||||
|
[[_tomcat_adapter]]
|
||||||
|
==== Tomcat 6, 7 and 8 Adapters
|
||||||
|
|
||||||
|
To be able to secure WAR apps deployed on Tomcat 6, 7 and 8 you must install the Keycloak Tomcat 6, 7 or 8 adapter into your Tomcat installation.
|
||||||
|
You then have to provide some extra configuration in each WAR you deploy to Tomcat.
|
||||||
|
Let's go over these steps.
|
||||||
|
|
||||||
|
[[_tomcat_adapter_installation]]
|
||||||
|
===== Adapter Installation
|
||||||
|
|
||||||
|
Adapters are no longer included with the appliance or war distribution.
|
||||||
|
Each adapter is a separate download on the Keycloak download site.
|
||||||
|
They are also available as a maven artifact.
|
||||||
|
|
||||||
|
You must unzip the adapter distro into Tomcat's `lib/` directory.
|
||||||
|
Including adapter's jars within your WEB-INF/lib directory will not work! The Keycloak adapter is implemented as a Valve and valve code must reside in Tomcat's main lib/ directory.
|
||||||
|
|
||||||
|
|
||||||
|
[source]
|
||||||
|
----
|
||||||
|
|
||||||
|
$ cd $TOMCAT_HOME/lib
|
||||||
|
$ unzip keycloak-tomcat6-adapter-dist.zip
|
||||||
|
or
|
||||||
|
$ unzip keycloak-tomcat7-adapter-dist.zip
|
||||||
|
or
|
||||||
|
$ unzip keycloak-tomcat8-adapter-dist.zip
|
||||||
|
----
|
||||||
|
|
||||||
|
===== Required Per WAR Configuration
|
||||||
|
|
||||||
|
This section describes how to secure a WAR directly by adding config and editing files within your WAR package.
|
||||||
|
|
||||||
|
The first thing you must do is create a `META-INF/context.xml` file in your WAR package.
|
||||||
|
This is a Tomcat specific config file and you must define a Keycloak specific Valve.
|
||||||
|
|
||||||
|
[source]
|
||||||
|
----
|
||||||
|
|
||||||
|
|
||||||
|
<Context path="/your-context-path">
|
||||||
|
<Valve className="org.keycloak.adapters.tomcat.KeycloakAuthenticatorValve"/>
|
||||||
|
</Context>
|
||||||
|
----
|
||||||
|
|
||||||
|
Next you must create a `keycloak.json` adapter config file within the `WEB-INF` directory of your WAR.
|
||||||
|
|
||||||
|
The format of this config file is describe in the <<fake/../java-adapter-config.adoc#_java_adapter_config,Java adapter configuration>>
|
||||||
|
|
||||||
|
Finally you must specify both a `login-config` and use standard servlet security to specify role-base constraints on your URLs.
|
||||||
|
Here's an example:
|
||||||
|
|
||||||
|
|
||||||
|
[source]
|
||||||
|
----
|
||||||
|
|
||||||
|
|
||||||
|
<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">
|
||||||
|
|
||||||
|
<module-name>customer-portal</module-name>
|
||||||
|
|
||||||
|
<security-constraint>
|
||||||
|
<web-resource-collection>
|
||||||
|
<web-resource-name>Customers</web-resource-name>
|
||||||
|
<url-pattern>/*</url-pattern>
|
||||||
|
</web-resource-collection>
|
||||||
|
<auth-constraint>
|
||||||
|
<role-name>user</role-name>
|
||||||
|
</auth-constraint>
|
||||||
|
</security-constraint>
|
||||||
|
|
||||||
|
<login-config>
|
||||||
|
<auth-method>BASIC</auth-method>
|
||||||
|
<realm-name>this is ignored currently</realm-name>
|
||||||
|
</login-config>
|
||||||
|
|
||||||
|
<security-role>
|
||||||
|
<role-name>admin</role-name>
|
||||||
|
</security-role>
|
||||||
|
<security-role>
|
||||||
|
<role-name>user</role-name>
|
||||||
|
</security-role>
|
||||||
|
</web-app>
|
||||||
|
----
|
373
securing_apps/topics/oidc/javascript-adapter.adoc
Normal file
373
securing_apps/topics/oidc/javascript-adapter.adoc
Normal file
|
@ -0,0 +1,373 @@
|
||||||
|
[[_javascript_adapter]]
|
||||||
|
=== Javascript Adapter
|
||||||
|
|
||||||
|
{{book.project.name}} comes with a client-side JavaScript library that can be used to secure HTML5/JavaScript applications. The JavaScript adapter has built-in
|
||||||
|
support for Cordova applications.
|
||||||
|
|
||||||
|
The library can be retrieved directly from the {{book.project.name}} server at `/auth/js/keycloak.js` and is also distributed as a ZIP archive.
|
||||||
|
|
||||||
|
It's recommended to load the JavaScript adapter directly from the ${book.project.name}} server as it will automatically be updated when you
|
||||||
|
upgrade the server. If you copy the adapter to your web application instead make sure you upgrade it after you have upgraded the server.
|
||||||
|
|
||||||
|
One important thing to note about using client-side applications is that the client has to be a public client as there is no secure way to store client
|
||||||
|
credentials in a client-side application. This makes it very important to make sure the redirect URIs you have configured for the client are correct and as
|
||||||
|
specific as possible.
|
||||||
|
|
||||||
|
To use the JavaScript adapter you must first create a client for your application in the {{book.project.name}} Administration Console. Make sure `public`
|
||||||
|
is selected for `Access Type`.
|
||||||
|
|
||||||
|
You also need to configure valid redirect URIs and valid web origins. Be as specific as possible as failing to do so may result in a security vulnerability.
|
||||||
|
|
||||||
|
Once the client is created click on the `Installation` tab select `Keycloak OIDC JSON` for `Format Option` then click on `Download`. The downloaded
|
||||||
|
`keycloak.json` file should be hosted on your web server at the same location as your HTML pages.
|
||||||
|
|
||||||
|
Alternatively, you can skip the configuration file and manually configure the adapter.
|
||||||
|
|
||||||
|
The following example shows how to initialize the JavaScript adapter:
|
||||||
|
|
||||||
|
[source,html]
|
||||||
|
----
|
||||||
|
<head>
|
||||||
|
<script src="keycloak.js"></script>
|
||||||
|
<script>
|
||||||
|
var keycloak = Keycloak();
|
||||||
|
keycloak.init().success(function(authenticated) {
|
||||||
|
alert(authenticated ? 'authenticated' : 'not authenticated');
|
||||||
|
}).error(function() {
|
||||||
|
alert('failed to initialize');
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
----
|
||||||
|
|
||||||
|
If the `keycloak.json` file is in a different location you can specify it:
|
||||||
|
|
||||||
|
[source,javascript]
|
||||||
|
----
|
||||||
|
var keycloak = Keycloak('http://localhost:8080/myapp/keycloak.json'));
|
||||||
|
----
|
||||||
|
|
||||||
|
Alternatively, you can pass in a JavaScript object with the required configuration instead:
|
||||||
|
|
||||||
|
[source,javascript]
|
||||||
|
----
|
||||||
|
var keycloak = Keycloak({
|
||||||
|
url: 'http://keycloak-server/auth',
|
||||||
|
realm: 'myrealm',
|
||||||
|
clientId: 'myapp'
|
||||||
|
});
|
||||||
|
----
|
||||||
|
|
||||||
|
By default to authenticate you need to call the `login` function. However, there are two options available to make the adapter automatically authenticate. You
|
||||||
|
can pass `login-required` or `check-sso` to the init function. `login-required` will authenticate the client if the user is logged-in to {{book.project.name}}
|
||||||
|
or display the login page if not. `check-sso` will only authenticate the client if the user is already logged-in, if the user is not logged-in the browser will be
|
||||||
|
redirected back to the application and remain unauthenticated.
|
||||||
|
|
||||||
|
To enable `login-required` set `onLoad` to `login-required` and pass to the init method:
|
||||||
|
|
||||||
|
[source]
|
||||||
|
----
|
||||||
|
keycloak.init({ onLoad: 'login-required' })
|
||||||
|
----
|
||||||
|
|
||||||
|
After the user is authenticated the application can make requests to RESTful services secured by {{book.project.name}} by including the bearer token in the
|
||||||
|
`Authorization` header. For example:
|
||||||
|
|
||||||
|
[source,javascript]
|
||||||
|
----
|
||||||
|
var loadData = function () {
|
||||||
|
document.getElementById('username').innerText = keycloak.username;
|
||||||
|
|
||||||
|
var url = 'http://localhost:8080/restful-service';
|
||||||
|
|
||||||
|
var req = new XMLHttpRequest();
|
||||||
|
req.open('GET', url, true);
|
||||||
|
req.setRequestHeader('Accept', 'application/json');
|
||||||
|
req.setRequestHeader('Authorization', 'Bearer ' + keycloak.token);
|
||||||
|
|
||||||
|
req.onreadystatechange = function () {
|
||||||
|
if (req.readyState == 4) {
|
||||||
|
if (req.status == 200) {
|
||||||
|
alert('Success');
|
||||||
|
} else if (req.status == 403) {
|
||||||
|
alert('Forbidden');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
req.send();
|
||||||
|
};
|
||||||
|
----
|
||||||
|
|
||||||
|
One thing to keep in mind is that the access token by default has a short life expiration so you may need to refresh the access token prior to sending the
|
||||||
|
request. You can do this by the `updateToken` method. The `updateToken` method returns a promise object which makes it easy to invoke the service only if the
|
||||||
|
token was successfully refreshed and for example display an error to the user if it wasn't. For example:
|
||||||
|
|
||||||
|
[source,javascript]
|
||||||
|
----
|
||||||
|
keycloak.updateToken(30).success(function() {
|
||||||
|
loadData();
|
||||||
|
}).error(function() {
|
||||||
|
alert('Failed to refresh token');
|
||||||
|
);
|
||||||
|
----
|
||||||
|
|
||||||
|
==== Session status iframe
|
||||||
|
|
||||||
|
By default, the JavaScript adapter creates a hidden iframe that is used to detect if a Single-Sign Out has occurred.
|
||||||
|
This does not require any network traffic, instead the status is retrieved by looking at a special status cookie.
|
||||||
|
This feature can be disabled by setting `checkLoginIframe: false` in the options passed to the `init` method.
|
||||||
|
|
||||||
|
You should not rely on looking at this cookie directly. It's format can change and it's also associated with the URL of the {{book.project.name}} server, not
|
||||||
|
your application.
|
||||||
|
|
||||||
|
[[_javascript_implicit_flow]]
|
||||||
|
==== Implicit and Hybrid Flow
|
||||||
|
|
||||||
|
By default, the JavaScript adapter uses the http://openid.net/specs/openid-connect-core-1_0.html#CodeFlowAuth[Authorization Code] flow.
|
||||||
|
|
||||||
|
With this flow the {{book.project.name}} server returns an authorization code, not an authentication token, to the application. The JavaScript adapter exchanges
|
||||||
|
the `code` for an access token and a refresh token after the browser is redirected back to the application.
|
||||||
|
|
||||||
|
{{book.project.name}} also supports the http://openid.net/specs/openid-connect-core-1_0.html#ImplicitFlowAuth[Implicit] flow where an access token
|
||||||
|
is sent immediately after successful authentication with {{book.project.name}}. This may have better performance than standard flow, as there is no additional
|
||||||
|
request to exchange the code for tokens, but it has implications when the access token expires.
|
||||||
|
|
||||||
|
However, sending the access token in the URL fragment can be a security vulnerability. For example the token could be leaked through web server logs and or
|
||||||
|
browser history.
|
||||||
|
|
||||||
|
To enable implicit flow, you need to enable the `Implicit Flow Enabled` flag for the client in the {{book.project.name}} Administration Console.
|
||||||
|
You also need to pass the parameter `flow` with value `implicit` to `init` method:
|
||||||
|
|
||||||
|
[source,javascript]
|
||||||
|
----
|
||||||
|
keycloak.init({ flow: 'implicit' })
|
||||||
|
----
|
||||||
|
|
||||||
|
One thing to note is that only an access token is provided and there is no refresh token. This means that once the access token has expired the application
|
||||||
|
has to do the redirect to the {{book.project.name}} again to obtain a new access token.
|
||||||
|
|
||||||
|
{{book.project.name}} also supports the http://openid.net/specs/openid-connect-core-1_0.html#HybridFlowAuth[Hybrid] flow.
|
||||||
|
|
||||||
|
This requires the client to have both the `Standard Flow Enabled` and `Implicit Flow Enabled` flags enabled in the admin console.
|
||||||
|
The {{book.project.name}} server will then send both the code and tokens to your application.
|
||||||
|
The access token can be used immediately while the code can be exchanged for access and refresh tokens.
|
||||||
|
Similar to the implicit flow, the hybrid flow is good for performance because the access token is available immediately.
|
||||||
|
But, the token is still sent in the URL, and the security vulnerability mentioned earlier may still apply.
|
||||||
|
|
||||||
|
One advantage in the Hybrid flow is that the refresh token is made available to the application.
|
||||||
|
|
||||||
|
For the Hybrid flow, you need to pass the parameter `flow` with value `hybrid` to the `init` method:
|
||||||
|
|
||||||
|
[source,javascript]
|
||||||
|
----
|
||||||
|
keycloak.init({ flow: 'hybrid' })
|
||||||
|
----
|
||||||
|
|
||||||
|
==== Older browsers
|
||||||
|
|
||||||
|
The JavaScript adapter depends on Base64 (window.btoa and window.atob) and HTML5 History API.
|
||||||
|
If you need to support browsers that don't have these available (for example IE9) you need to add polyfillers.
|
||||||
|
|
||||||
|
Example polyfill libraries:
|
||||||
|
|
||||||
|
* https://github.com/davidchambers/Base64.js
|
||||||
|
* https://github.com/devote/HTML5-History-API
|
||||||
|
|
||||||
|
==== JavaScript Adapter reference
|
||||||
|
|
||||||
|
===== Constructor
|
||||||
|
|
||||||
|
[source]
|
||||||
|
----
|
||||||
|
|
||||||
|
new Keycloak();
|
||||||
|
new Keycloak('http://localhost/keycloak.json');
|
||||||
|
new Keycloak({ url: 'http://localhost/auth', realm: 'myrealm', clientId: 'myApp' });
|
||||||
|
----
|
||||||
|
|
||||||
|
===== Properties
|
||||||
|
|
||||||
|
authenticated::
|
||||||
|
Is `true` if the user is authenticated, `false` otherwise.
|
||||||
|
|
||||||
|
token::
|
||||||
|
The base64 encoded token that can be sent in the `Authorization` header in requests to services.
|
||||||
|
|
||||||
|
tokenParsed::
|
||||||
|
The parsed token as a JavaScript object.
|
||||||
|
|
||||||
|
subject::
|
||||||
|
The user id.
|
||||||
|
|
||||||
|
idToken::
|
||||||
|
The base64 encoded ID token.
|
||||||
|
|
||||||
|
idTokenParsed::
|
||||||
|
The parsed id token as a JavaScript object.
|
||||||
|
|
||||||
|
realmAccess::
|
||||||
|
The realm roles associated with the token.
|
||||||
|
|
||||||
|
resourceAccess::
|
||||||
|
The resource roles assocaited with the token.
|
||||||
|
|
||||||
|
refreshToken::
|
||||||
|
The base64 encoded refresh token that can be used to retrieve a new token.
|
||||||
|
|
||||||
|
refreshTokenParsed::
|
||||||
|
The parsed refresh token as a JavaScript object.
|
||||||
|
|
||||||
|
timeSkew::
|
||||||
|
The estimated time difference between the browser time and the {{book.project.name}} server in seconds. This value is just an estimation, but is accurate
|
||||||
|
enough when determining if a token is expired or not.
|
||||||
|
|
||||||
|
responseMode::
|
||||||
|
Response mode passed in init (default value is fragment).
|
||||||
|
|
||||||
|
flow::
|
||||||
|
Flow passed in init.
|
||||||
|
|
||||||
|
responseType::
|
||||||
|
Response type sent to {{book.project.name}} with login requests. This is determined based on the flow value used during initialization, but can be overridden by setting this value.
|
||||||
|
|
||||||
|
===== Methods
|
||||||
|
|
||||||
|
====== init(options)
|
||||||
|
|
||||||
|
Called to initialize the adapter.
|
||||||
|
|
||||||
|
Options is an Object, where:
|
||||||
|
|
||||||
|
* onLoad - Specifies an action to do on load. Supported values are 'login-required' or 'check-sso'.
|
||||||
|
* token - Set an initial value for the token.
|
||||||
|
* refreshToken - Set an initial value for the refresh token.
|
||||||
|
* idToken - Set an initial value for the id token (only together with token or refreshToken).
|
||||||
|
* timeSkew - Set an initial value for skew between local time and {{book.project.name}} server in seconds (only together with token or refreshToken).
|
||||||
|
* checkLoginIframe - Set to enable/disable monitoring login state (default is true).
|
||||||
|
* checkLoginIframeInterval - Set the interval to check login state (default is 5 seconds).
|
||||||
|
* responseMode - Set the OpenID Connect response mode send to {{book.project.name}} server at login request. Valid values are query or fragment . Default value is fragment, which means that after successful authentication will {{book.project.name}} redirect to javascript application with OpenID Connect parameters added in URL fragment. This is generally safer and recommended over query.
|
||||||
|
* flow - Set the OpenID Connect flow. Valid values are standard, implicit or hybrid.
|
||||||
|
|
||||||
|
Returns promise to set functions to be invoked on success or error.
|
||||||
|
|
||||||
|
====== login(options)
|
||||||
|
|
||||||
|
Redirects to login form on (options is an optional object with redirectUri and/or prompt fields).
|
||||||
|
|
||||||
|
Options is an Object, where:
|
||||||
|
|
||||||
|
* redirectUri - Specifies the uri to redirect to after login.
|
||||||
|
* prompt - By default the login screen is displayed if the user is not logged-in to {{book.project.name}}. To only authenticate to the application if the user is already logged-in and not display the login page if the user is not logged-in, set this option to `none`. To always require re-authentication and ignore SSO, set this option to `login` .
|
||||||
|
* maxAge - Used just if user is already authenticated. Specifies maximum time since the authentication of user happened. If user is already authenticated for longer time than `maxAge`, the SSO is ignored and he will need to re-authenticate again.
|
||||||
|
* loginHint - Used to pre-fill the username/email field on the login form.
|
||||||
|
* action - If value is 'register' then user is redirected to registration page, otherwise to login page.
|
||||||
|
* locale - Specifies the desired locale for the UI.
|
||||||
|
|
||||||
|
====== createLoginUrl(options)
|
||||||
|
|
||||||
|
Returns the URL to login form on (options is an optional object with redirectUri and/or prompt fields).
|
||||||
|
|
||||||
|
Options is an Object, which supports same options like the function `login` .
|
||||||
|
|
||||||
|
====== logout(options)
|
||||||
|
|
||||||
|
Redirects to logout.
|
||||||
|
|
||||||
|
Options is an Object, where:
|
||||||
|
|
||||||
|
* redirectUri - Specifies the uri to redirect to after logout.
|
||||||
|
|
||||||
|
====== createLogoutUrl(options)
|
||||||
|
|
||||||
|
Returns the URL to logout the user.
|
||||||
|
|
||||||
|
Options is an Object, where:
|
||||||
|
|
||||||
|
* redirectUri - Specifies the uri to redirect to after logout.
|
||||||
|
|
||||||
|
====== register(options)
|
||||||
|
|
||||||
|
Redirects to registration form. Shortcut for login with option action = 'register'
|
||||||
|
|
||||||
|
Options are same as for the login method but 'action' is set to 'register'
|
||||||
|
|
||||||
|
====== createRegisterUrl(options)
|
||||||
|
|
||||||
|
Returns the url to registration page. Shortcut for createLoginUrl with option action = 'register'
|
||||||
|
|
||||||
|
Options are same as for the createLoginUrl method but 'action' is set to 'register'
|
||||||
|
|
||||||
|
====== accountManagement()
|
||||||
|
|
||||||
|
Redirects to the Account Management Console.
|
||||||
|
|
||||||
|
====== createAccountUrl()
|
||||||
|
|
||||||
|
Returns the URL to the Account Management Console.
|
||||||
|
|
||||||
|
====== hasRealmRole(role)
|
||||||
|
|
||||||
|
Returns true if the token has the given realm role.
|
||||||
|
|
||||||
|
====== hasResourceRole(role, resource)
|
||||||
|
|
||||||
|
Returns true if the token has the given role for the resource (resource is optional, if not specified clientId is used).
|
||||||
|
|
||||||
|
====== loadUserProfile()
|
||||||
|
|
||||||
|
Loads the users profile.
|
||||||
|
|
||||||
|
Returns promise to set functions to be invoked on success or error.
|
||||||
|
|
||||||
|
====== isTokenExpired(minValidity)
|
||||||
|
|
||||||
|
Returns true if the token has less than minValidity seconds left before it expires (minValidity is optional, if not specified 0 is used).
|
||||||
|
|
||||||
|
====== updateToken(minValidity)
|
||||||
|
|
||||||
|
If the token expires within minValidity seconds (minValidity is optional, if not specified 0 is used) the token is refreshed.
|
||||||
|
If the session status iframe is enabled, the session status is also checked.
|
||||||
|
|
||||||
|
Returns promise to set functions that can be invoked if the token is still valid, or if the token is no longer valid.
|
||||||
|
For example:
|
||||||
|
|
||||||
|
[source,javascript]
|
||||||
|
----
|
||||||
|
keycloak.updateToken(5).success(function(refreshed) {
|
||||||
|
if (refreshed) {
|
||||||
|
alert('Token was successfully refreshed');
|
||||||
|
} else {
|
||||||
|
alert('Token is still valid');
|
||||||
|
}
|
||||||
|
}).error(function() {
|
||||||
|
alert('Failed to refresh the token, or the session has expired');
|
||||||
|
});
|
||||||
|
----
|
||||||
|
|
||||||
|
====== clearToken()
|
||||||
|
|
||||||
|
Clear authentication state, including tokens.
|
||||||
|
This can be useful if application has detected the session was expired, for example if updating token fails.
|
||||||
|
|
||||||
|
Invoking this results in onAuthLogout callback listener being invoked.
|
||||||
|
|
||||||
|
===== Callback Events
|
||||||
|
|
||||||
|
The adapter supports setting callback listeners for certain events.
|
||||||
|
|
||||||
|
For example:
|
||||||
|
[source]
|
||||||
|
----
|
||||||
|
keycloak.onAuthSuccess = function() { alert('authenticated'); }
|
||||||
|
----
|
||||||
|
|
||||||
|
The available events are:
|
||||||
|
|
||||||
|
* onReady(authenticated) - Called when the adapter is initialized.
|
||||||
|
* onAuthSuccess - Called when a user is successfully authenticated.
|
||||||
|
* onAuthError - Called if there was an error during authentication.
|
||||||
|
* onAuthRefreshSuccess - Called when the token is refreshed.
|
||||||
|
* onAuthRefreshError - Called if there was an error while trying to refresh the token.
|
||||||
|
* onAuthLogout - Called if the user is logged out (will only be called if the session status iframe is enabled, or in Cordova mode).
|
||||||
|
* onTokenExpired - Called when the access token is expired. If a refresh token is available the token can be refreshed with updateToken, or in cases where it's not (ie. with implicit flow) you can redirect to login screen to obtain a new access token.
|
49
securing_apps/topics/oidc/mod-auth-openidc.adoc
Normal file
49
securing_apps/topics/oidc/mod-auth-openidc.adoc
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
[[_mod_auth_openidc]]
|
||||||
|
==== mod_auth_openidc Apache HTTPD Module
|
||||||
|
|
||||||
|
The https://github.com/pingidentity/mod_auth_openidc[mod_auth_openidc] is an Apache HTTP plugin for OpenID Connect. If your language/environment supports using Apache HTTPD
|
||||||
|
as a proxy, then you can use _mod_auth_openidc_ to secure your web application with OpenID Connect. Configuration of this module
|
||||||
|
is beyond the scope of this document. Please see the _mod_auth_openidc_ Github repo for more details on configuration.
|
||||||
|
|
||||||
|
To configure _mod_auth_openidc_ you'll need
|
||||||
|
|
||||||
|
* The client_id.
|
||||||
|
* The client_secret.
|
||||||
|
* The redirect_uri to your application.
|
||||||
|
* The Keycloak openid-configuration url
|
||||||
|
* _mod_auth_openidc_ specific Apache HTTPD module config.
|
||||||
|
|
||||||
|
An example configuration would look like the following.
|
||||||
|
|
||||||
|
[source,xml]
|
||||||
|
----
|
||||||
|
LoadModule auth_openidc_module modules/mod_auth_openidc.so
|
||||||
|
|
||||||
|
ServerName ${HOSTIP}
|
||||||
|
|
||||||
|
<VirtualHost *:80>
|
||||||
|
|
||||||
|
ServerAdmin webmaster@localhost
|
||||||
|
DocumentRoot /var/www/html
|
||||||
|
|
||||||
|
#this is required by mod_auth_openidc
|
||||||
|
OIDCCryptoPassphrase a-random-secret-used-by-apache-oidc-and-balancer
|
||||||
|
|
||||||
|
OIDCProviderMetadataURL ${KC_ADDR}/auth/realms/${KC_REALM}/.well-known/openid-configuration
|
||||||
|
|
||||||
|
OIDCClientID ${CLIENT_ID}
|
||||||
|
OIDCClientSecret ${CLIENT_SECRET}
|
||||||
|
OIDCRedirectURI http://${HOSTIP}/${CLIENT_APP_NAME}/redirect_uri
|
||||||
|
|
||||||
|
# maps the prefered_username claim to the REMOTE_USER environment variable
|
||||||
|
OIDCRemoteUserClaim preferred_username
|
||||||
|
|
||||||
|
<Location /${CLIENT_APP_NAME}/>
|
||||||
|
AuthType openid-connect
|
||||||
|
Require valid-user
|
||||||
|
</Location>
|
||||||
|
</VirtualHost>
|
||||||
|
----
|
||||||
|
|
||||||
|
Further information on how to configure mod_auth_openidc can be found on the https://github.com/pingidentity/mod_auth_openidc[mod_auth_openidc]
|
||||||
|
project page.
|
139
securing_apps/topics/oidc/nodejs-adapter.adoc
Normal file
139
securing_apps/topics/oidc/nodejs-adapter.adoc
Normal file
|
@ -0,0 +1,139 @@
|
||||||
|
[[_nodejs_adapter]]
|
||||||
|
=== Node.js Adapter
|
||||||
|
|
||||||
|
{{book.project.name}} provides a Node.js adapter built on top of https://github.com/senchalabs/connect[Connect] to protect server side JavaScript apps — the goal was to be flexible enough to integrate with frameworks like https://expressjs.com/[Express.js].
|
||||||
|
|
||||||
|
{% if book.community %}
|
||||||
|
The library can be downloaded directly from https://www.npmjs.com/package/keycloak-connect[ {{book.project.name}} organization] and the source is available at
|
||||||
|
https://github.com/keycloak/keycloak-nodejs-connect[GitHub].
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
To use the Node.js adapter, first you must create a client for your application in the {{book.project.name}} Administration Console. The adapter supports public, confidential and bearer-only access type. Which one to choose depends on the use-case scenario.
|
||||||
|
|
||||||
|
Once the client is created click on the `Installation` tab select `{{book.project.name}} OIDC JSON` for `Format Option` then click on `Download`. The downloaded `keycloak.json` file should be at the root folder of your project.
|
||||||
|
|
||||||
|
==== Installation
|
||||||
|
|
||||||
|
Assuming you've already installed https://nodejs.org[Node.js], create a folder for your application:
|
||||||
|
|
||||||
|
mkdir myapp && cd myapp
|
||||||
|
|
||||||
|
Use `npm init` command to create a `package.json` for your application. Now add the {{book.project.name}} connect adapter in the dependencies list:
|
||||||
|
|
||||||
|
{% if book.community %}
|
||||||
|
|
||||||
|
[source,json,subs="attributes"]
|
||||||
|
----
|
||||||
|
dependencies": {
|
||||||
|
"keycloak-connect": "{{book.project.versionMvn}}"
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if book.product %}
|
||||||
|
|
||||||
|
[source,json,subs="attributes"]
|
||||||
|
----
|
||||||
|
dependencies": {
|
||||||
|
"keycloak-connect": "file:rh-sso-{{book.project.version}}-nodejs-adapter.tgz"
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
==== Usage
|
||||||
|
Instantiate a Keycloak class::
|
||||||
|
|
||||||
|
The `Keycloak` class provides a central point for configuration
|
||||||
|
and integration with your application. The simplest creation
|
||||||
|
involves no arguments.
|
||||||
|
|
||||||
|
var keycloak = new Keycloak();
|
||||||
|
|
||||||
|
By default, this will locate a file named `keycloak.json` alongside
|
||||||
|
the main executable of your application to initialize keycloak-specific
|
||||||
|
settings (public key, realm name, various URLs). The `keycloak.json` file
|
||||||
|
is obtained from the {{book.project.name}} Admin Console.
|
||||||
|
|
||||||
|
Instantiation with this method results in all of the reasonable defaults
|
||||||
|
being used.
|
||||||
|
|
||||||
|
Configuring a web session store::
|
||||||
|
|
||||||
|
If you wish to use web sessions to manage
|
||||||
|
server-side state for authentication, you will need to initialize the
|
||||||
|
`Keycloak(...)` with at least a `store` parameter, passing in the actual
|
||||||
|
session store that `express-session` is using.
|
||||||
|
|
||||||
|
var session = require('express-session');
|
||||||
|
var memoryStore = new session.MemoryStore();
|
||||||
|
|
||||||
|
var keycloak = new Keycloak({ store: memoryStore });
|
||||||
|
|
||||||
|
Passing a custom scope value::
|
||||||
|
|
||||||
|
By default, the scope value `openid` will be passed as query parameter to {{book.project.name}}'s login URL but you can add an additional custom value :
|
||||||
|
|
||||||
|
var keycloak = new Keycloak({ scope: 'offline_access' });
|
||||||
|
|
||||||
|
==== Install middleware
|
||||||
|
|
||||||
|
Once instantiated, install the middleware into your connect-capable app:
|
||||||
|
|
||||||
|
var app = express();
|
||||||
|
|
||||||
|
app.use( keycloak.middleware() );
|
||||||
|
|
||||||
|
==== Protect resources
|
||||||
|
|
||||||
|
Simple authentication::
|
||||||
|
|
||||||
|
To enforce that an user must be authenticated before accessing a resource,
|
||||||
|
simply use a no-argument version of `keycloak.protect()`:
|
||||||
|
|
||||||
|
app.get( '/complain', keycloak.protect(), complaintHandler );
|
||||||
|
|
||||||
|
Role-based authorization::
|
||||||
|
|
||||||
|
To secure a resource with an application role for the current app:
|
||||||
|
|
||||||
|
app.get( '/special', keycloak.protect('special'), specialHandler );
|
||||||
|
|
||||||
|
To secure a resource with an application role for a *different* app:
|
||||||
|
|
||||||
|
app.get( '/extra-special', keycloak.protect('other-app:special', extraSpecialHandler );
|
||||||
|
|
||||||
|
To secure a resource with a realm role:
|
||||||
|
|
||||||
|
app.get( '/admin', keycloak.protect( 'realm:admin' ), adminHandler );
|
||||||
|
|
||||||
|
Advanced authorization::
|
||||||
|
|
||||||
|
To secure resources based on parts of the URL itself, assuming a role exists
|
||||||
|
for each section:
|
||||||
|
|
||||||
|
function protectBySection(token, request) {
|
||||||
|
return token.hasRole( request.params.section );
|
||||||
|
}
|
||||||
|
|
||||||
|
app.get( '/:section/:page', keycloak.protect( protectBySection ), sectionHandler );
|
||||||
|
|
||||||
|
==== Additional URLs
|
||||||
|
|
||||||
|
Explicit user-triggered logout::
|
||||||
|
|
||||||
|
By default, the middleware catches calls to `/logout` to send the user through a
|
||||||
|
{{book.project.name}}-centric logout workflow. This can be changed by specifying a `logout`
|
||||||
|
configuration parameter to the `middleware()` call:
|
||||||
|
|
||||||
|
app.use( keycloak.middleware( { logout: '/logoff' } ));
|
||||||
|
|
||||||
|
{{book.project.name}} Admin Callbacks::
|
||||||
|
|
||||||
|
Also, the middleware supports callbacks from the {{book.project.name}} console to logout a single
|
||||||
|
session or all sessions. By default, these type of admin callbacks occur relative
|
||||||
|
to the root URL of `/` but can be changed by providing an `admin` parameter
|
||||||
|
to the `middleware()` call:
|
||||||
|
|
||||||
|
app.use( keycloak.middleware( { admin: '/callbacks' } );
|
193
securing_apps/topics/oidc/oidc-generic.adoc
Normal file
193
securing_apps/topics/oidc/oidc-generic.adoc
Normal file
|
@ -0,0 +1,193 @@
|
||||||
|
=== Other OpenID Connect libraries
|
||||||
|
|
||||||
|
{{book.project.name}} can be secured by supplied adapters that usually are easier to use and provide better integration with {{book.project.name}}. However,
|
||||||
|
if there is no adapter available for your programming language, framework or platform you may opt to use a generic OpenID Connect Resource Provider (RP) library
|
||||||
|
instead. This chapter describes details specific to {{book.project.name}} and doesn't go into low-level details of the protocols. For more details refer to the
|
||||||
|
http://openid.net/connect/[OpenID Connect specifications] and https://tools.ietf.org/html/rfc6749[OAuth2 specification].
|
||||||
|
|
||||||
|
==== Endpoints
|
||||||
|
|
||||||
|
The most important endpoint to know is the `well-known` configuration endpoint. It lists endpoints and other configuration options relevant to the OpenID
|
||||||
|
Connect implementation in {{book.project.name}}. The endpoint is:
|
||||||
|
|
||||||
|
....
|
||||||
|
/realms/{realm-name}/.well-known/openid-configuration
|
||||||
|
....
|
||||||
|
|
||||||
|
To get the full URL add the base URL for {{book.project.name}} and replace `{realm-name}` with the name of your realm. For example:
|
||||||
|
|
||||||
|
$$http://localhost:8080/auth/realms/master/.well-known/openid-configuration$$
|
||||||
|
|
||||||
|
Some RP libraries will retrieve all required endpoints from this endpoint, but for others you may need to list the endpoints individually.
|
||||||
|
|
||||||
|
===== Authorization Endpoint
|
||||||
|
....
|
||||||
|
/realms/{realm-name}/protocol/openid-connect/auth
|
||||||
|
....
|
||||||
|
|
||||||
|
Performs authentication of the end-user. This is done by redirecting user agent to this endpoint.
|
||||||
|
|
||||||
|
For more details see http://openid.net/specs/openid-connect-core-1_0.html#AuthorizationEndpoint[Authorization Endpoint] section in OpenID Connect specification.
|
||||||
|
|
||||||
|
===== Token Endpoint
|
||||||
|
....
|
||||||
|
/realms/{realm-name}/protocol/openid-connect/token
|
||||||
|
....
|
||||||
|
|
||||||
|
Used to obtain tokens. Tokens can either be obtained by exchanging an authorization code or by supplying credentials directly depending on what flow is used.
|
||||||
|
The token endpoint is also used to obtain new access tokens when they expire.
|
||||||
|
|
||||||
|
For more details see http://openid.net/specs/openid-connect-core-1_0.html#TokenEndpoint[Token Endpoint] section in OpenID Connect specification.
|
||||||
|
|
||||||
|
===== Userinfo Endpoint
|
||||||
|
....
|
||||||
|
/realms/{realm-name}/protocol/openid-connect/userinfo
|
||||||
|
....
|
||||||
|
|
||||||
|
Returns standard claims about the authenticated user. Protected by a bearer token.
|
||||||
|
|
||||||
|
For more details see http://openid.net/specs/openid-connect-core-1_0.html#UserInfo[Userinfo Endpoint] section in OpenID Connect specification.
|
||||||
|
|
||||||
|
===== Logout Endpoint
|
||||||
|
....
|
||||||
|
/realms/{realm-name}/protocol/openid-connect/logout
|
||||||
|
....
|
||||||
|
|
||||||
|
Logs out the authenticated user.
|
||||||
|
|
||||||
|
User agent can be redirected to the endpoint in which case the active user session will be logged out. Afterwards the user agent is redirected back to the application.
|
||||||
|
|
||||||
|
The endpoint can also be invoked directly by the application. To invoke this endpoint directly the refresh token needs to be included as well as credentials
|
||||||
|
required to authenticate the client.
|
||||||
|
|
||||||
|
===== Certificate Endpoint
|
||||||
|
....
|
||||||
|
/realms/{realm-name}/protocol/openid-connect/certs
|
||||||
|
....
|
||||||
|
|
||||||
|
Public key used by realm encoded as a JSON Web Key (JWK). This key can be used to verify tokens issued by {{book.project.name}} without making invocations to
|
||||||
|
the server.
|
||||||
|
|
||||||
|
For more details see https://tools.ietf.org/html/rfc7517[JSON Web Key specification].
|
||||||
|
|
||||||
|
===== Introspection Endpoint
|
||||||
|
....
|
||||||
|
/realms/{realm-name}/protocol/openid-connect/token/introspect
|
||||||
|
....
|
||||||
|
|
||||||
|
Used to retrieve the active state of a token. Protected by a bearer token and can only be invoked by confidential clients.
|
||||||
|
|
||||||
|
For more details see https://tools.ietf.org/html/rfc7662[OAuth 2.0 Token Introspection specification].
|
||||||
|
|
||||||
|
===== Dynamic Client Registration Endpoint
|
||||||
|
....
|
||||||
|
/realms/{realm-name}/clients-registrations/openid-connect
|
||||||
|
....
|
||||||
|
|
||||||
|
Used to dynamically register clients.
|
||||||
|
|
||||||
|
For more details see <<fake/../../client-registration.adoc#_client_registration,Client Registration chapter>> and the
|
||||||
|
https://openid.net/specs/openid-connect-registration-1_0.html[OpenID Connect Dynamic Client Registration specification].
|
||||||
|
|
||||||
|
|
||||||
|
==== Flows
|
||||||
|
|
||||||
|
===== Authorization Code
|
||||||
|
|
||||||
|
The Authorization Code flow redirects the user agent to {{book.project.name}}. Once the user has successfully authenticated with {{book.project.name}} an
|
||||||
|
Authorization Code is created and the user agent is redirected back to the application. The application then uses the authorization code along with its
|
||||||
|
credentials to obtain an Access Token, Refresh Token and ID Token from {{book.project.name}}.
|
||||||
|
|
||||||
|
The flow is targeted towards web applications, but is also recommended for native applications, including mobile applications, where it is possible to embed
|
||||||
|
a user agent.
|
||||||
|
|
||||||
|
For more details refer to the http://openid.net/specs/openid-connect-core-1_0.html#CodeFlowAuth[Authorization Code Flow] in the OpenID Connect specification.
|
||||||
|
|
||||||
|
===== Implicit
|
||||||
|
|
||||||
|
The Implicit flow redirects works similarly to the Authorization Code flow, but instead of returning a Authorization Code the Access Token and ID Token is
|
||||||
|
returned. This reduces the need for the extra invocation to exchange the Authorization Code for an Access Token. However, it does not include a Refresh
|
||||||
|
Token. This results in the need to either permit Access Tokens with a long expiration, which is problematic as it's very hard to invalidate these. Or
|
||||||
|
requires a new redirect to obtain new Access Token once the initial Access Token has expired. The Implicit flow is useful if the application only wants to
|
||||||
|
authenticate the user and deals with logout itself.
|
||||||
|
|
||||||
|
There's also a Hybrid flow where both the Access Token and an Authorization Code is returned.
|
||||||
|
|
||||||
|
One thing to note is that both the Implicit flow and Hybrid flow has potential security risks as the Access Token may be leaked through web server logs and
|
||||||
|
browser history. This is somewhat mitigated by using short expiration for Access Tokens.
|
||||||
|
|
||||||
|
For more details refer to the http://openid.net/specs/openid-connect-core-1_0.html#ImplicitFlowAuth[Implicit Flow] in the OpenID Connect specification.
|
||||||
|
|
||||||
|
[[_resource_owner_password_credentials_flow]]
|
||||||
|
===== Resource Owner Password Credentials
|
||||||
|
|
||||||
|
Resource Owner Password Credentials, referred to as Direct Grant in {{book.project.name}}, allows exchanging user credentials for tokens. It's not recommended
|
||||||
|
to use this flow unless you absolutely need to. Examples where this could be useful are legacy applications and command-line interfaces.
|
||||||
|
|
||||||
|
There are a number of limitations of using this flow, including:
|
||||||
|
|
||||||
|
* User credentials are exposed to the application
|
||||||
|
* Applications need login pages
|
||||||
|
* Application needs to be aware of the authentication scheme
|
||||||
|
* Changes to authentication flow requires changes to application
|
||||||
|
* No support for identity brokering or social login
|
||||||
|
* Flows are not supported (user self-registration, required actions, etc.)
|
||||||
|
|
||||||
|
For a client to be permitted to use the Resource Owner Password Credentials grant the client has to have the `Direct Access Grants Enabled` option enabled.
|
||||||
|
|
||||||
|
This flow is not included in OpenID Connect, but is a part of the OAuth 2.0 specification.
|
||||||
|
|
||||||
|
For more details refer to the https://tools.ietf.org/html/rfc6749#section-4.3[Resource Owner Password Credentials Grant] chapter in the OAuth 2.0 specification.
|
||||||
|
|
||||||
|
====== Example using CURL
|
||||||
|
|
||||||
|
The following example shows how to obtain an access token for a user in the realm `master` with username `user` and password `password`. The example is using
|
||||||
|
the confidential client `myclient`:
|
||||||
|
|
||||||
|
[source,bash]
|
||||||
|
----
|
||||||
|
curl \
|
||||||
|
-d "client_id=myclient" \
|
||||||
|
-d "client_secret=40cc097b-2a57-4c17-b36a-8fdf3fc2d578" \
|
||||||
|
-d "username=user" \
|
||||||
|
-d "password=password" \
|
||||||
|
-d "grant_type=password" \
|
||||||
|
"http://localhost:8080/auth/realms/master/protocol/openid-connect/token"
|
||||||
|
----
|
||||||
|
|
||||||
|
===== Client Credentials
|
||||||
|
|
||||||
|
Client Credentials is used when clients (applications and services) wants to obtain access on behalf of themselves rather than on behalf of a user. This can
|
||||||
|
for example be useful for background services that applies changes to the system in general rather than for a specific user.
|
||||||
|
|
||||||
|
{{book.project.name}} provides support for clients to authenticate either with a secret or with public/private keys.
|
||||||
|
|
||||||
|
This flow is not included in OpenID Connect, but is a part of the OAuth 2.0 specification.
|
||||||
|
|
||||||
|
For more details refer to the https://tools.ietf.org/html/rfc6749#section-4.4[Client Credentials Grant] chapter in the OAuth 2.0 specification.
|
||||||
|
|
||||||
|
==== Redirect URIs
|
||||||
|
|
||||||
|
When using the redirect based flows it's important to use valid redirect uris for your clients. The redirect uris should be as specific as possible. This
|
||||||
|
especially applies to client-side (public clients) applications. Failing to do so could result in:
|
||||||
|
|
||||||
|
* Open redirects - this can allow attackers to create spoof links that looks like they are coming from your domain
|
||||||
|
* Unauthorized entry - when users are already authenticated with {{book.project.name}} an attacker can use a public client where redirect uris have not be configured correctly to gain access by redirecting the user without the users knowledge
|
||||||
|
|
||||||
|
In production for web applications always use `https` for all redirect URIs. Do not allow redirects to http.
|
||||||
|
|
||||||
|
There's also a few special redirect URIs:
|
||||||
|
|
||||||
|
[[_installed_applications_url]]
|
||||||
|
`$$http://localhost$$`::
|
||||||
|
|
||||||
|
This redirect URI is useful for native applications and allows the native application to create a web server on a random port that can be used to obtain the
|
||||||
|
authorization code. This redirect uri allows any port.
|
||||||
|
|
||||||
|
[[_installed_applications_urn]]
|
||||||
|
`urn:ietf:wg:oauth:2.0:oob`::
|
||||||
|
|
||||||
|
If its not possible to start a web server in the client (or a browser is not available) it is possible to use the special `urn:ietf:wg:oauth:2.0:oob` redirect uri.
|
||||||
|
When this redirect uri is used {{book.project.name}} displays a page with the code in the title and in a box on the page.
|
||||||
|
The application can either detect that the browser title has changed, or the user can copy/paste the code manually to the application.
|
||||||
|
With this redirect uri it is also possible for a user to use a different device to obtain a code to paste back to the application.
|
4
securing_apps/topics/oidc/oidc-overview.adoc
Normal file
4
securing_apps/topics/oidc/oidc-overview.adoc
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
== OpenID Connect
|
||||||
|
|
||||||
|
This section describes how you can secure applications and services with OpenID Connect using either {{book.project.name}} adapters or generic OpenID Connect
|
||||||
|
Resource Provider libraries.
|
9
securing_apps/topics/overview/overview.adoc
Normal file
9
securing_apps/topics/overview/overview.adoc
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
== Overview
|
||||||
|
|
||||||
|
{{book.project.name}} supports both OpenID Connect (an extension to OAuth 2.0) and SAML 2.0. When securing clients and services the first thing you need to
|
||||||
|
decide is which of the two you are going to use. If you want you can also choose to secure some with OpenID Connect and others with SAML.
|
||||||
|
|
||||||
|
To secure clients and services you are also going to need an adapter or library for the protocol you've selected. {{book.project.name}} comes with its own
|
||||||
|
adapters for selected platforms, but it is also possible to use generic OpenID Connect Resource Provider and SAML Service Provider libraries.
|
||||||
|
|
||||||
|
|
79
securing_apps/topics/overview/supported-platforms.adoc
Normal file
79
securing_apps/topics/overview/supported-platforms.adoc
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
=== Supported Platforms
|
||||||
|
|
||||||
|
==== OpenID Connect
|
||||||
|
|
||||||
|
===== Java
|
||||||
|
* <<fake/../../oidc/java/jboss-adapter.adoc#_jboss_adapter,JBoss EAP>>
|
||||||
|
{% if book.community %}
|
||||||
|
* <<fake/../../oidc/java/jboss-adapter.adoc#_jboss_adapter,WildFly>>
|
||||||
|
{% endif %}
|
||||||
|
* <<fake/../../oidc/java/fuse-adapter.adoc#_fuse_adapter,Fuse>>
|
||||||
|
{% if book.community %}
|
||||||
|
* <<fake/../../oidc/java/tomcat-adapter.adoc#_tomcat_adapter,Tomcat>>
|
||||||
|
* <<fake/../../oidc/java/jetty9-adapter.adoc#_jetty9_adapter,Jetty 9>>, <<fake/../../oidc/java/jetty8-adapter.adoc#_jetty8_adapter,Jetty 8>>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if book.community %}
|
||||||
|
* <<fake/../../oidc/java/servlet-filter-adapter.adoc#_servlet_filter_adapter,Servlet Filter>>
|
||||||
|
* <<fake/../../oidc/java/spring-security-adapter.adoc#_spring_security_adapter,Spring Security>> (community)
|
||||||
|
* <<fake/../../oidc/java/spring-boot-adapter.adoc#_spring_boot_adapter,Spring Boot>> (community)
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if book.community %}
|
||||||
|
===== JavaScript (client-side)
|
||||||
|
* <<fake/../../oidc/javascript-adapter.adoc#_javascript_adapter,JavaScript>>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
===== Node.js (server-side)
|
||||||
|
* <<fake/../../oidc/nodejs-adapter.adoc#_nodejs_adapter,Node.js>>
|
||||||
|
|
||||||
|
===== JavaScript
|
||||||
|
* <<fake/../../oidc/javascript-adapter.adoc#_javascript_adapter,JavaScript>>
|
||||||
|
|
||||||
|
{% if book.community %}
|
||||||
|
===== Node.js
|
||||||
|
* https://github.com/keycloak/keycloak-nodejs-connect[{{book.project.name}} Connect] (community)
|
||||||
|
* https://github.com/keycloak/keycloak-nodejs-auth-utils[{{book.project.name}} Auth Utils] (community)
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if book.community %}
|
||||||
|
==== C#
|
||||||
|
* https://github.com/dylanplecki/KeycloakOwinAuthentication[OWIN] (community)
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if book.community %}
|
||||||
|
==== Python
|
||||||
|
* https://pypi.python.org/pypi/python-openid/[python-openid] (generic)
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if book.community %}
|
||||||
|
==== Android
|
||||||
|
* https://github.com/openid/AppAuth-Android[AppAuth] (generic)
|
||||||
|
* https://github.com/aerogear/aerogear-android-authz[AeroGear] (generic)
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if book.community %}
|
||||||
|
==== iOS
|
||||||
|
* https://github.com/openid/AppAuth-iOS[AppAuth] (generic)
|
||||||
|
* https://github.com/aerogear/aerogear-ios-oauth2[AeroGear] (generic)
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if book.community %}
|
||||||
|
===== Apache HTTP Server
|
||||||
|
* https://github.com/pingidentity/mod_auth_openidc[mod_auth_openidc]
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
==== SAML
|
||||||
|
|
||||||
|
===== Java
|
||||||
|
|
||||||
|
* <<fake/../../saml/java/saml-jboss-adapter.adoc#_saml_jboss_adapter,JBoss EAP>>
|
||||||
|
{% if book.community %}
|
||||||
|
* <<fake/../../saml/java/saml-jboss-adapter.adoc#_saml_jboss_adapter,WildFly>>
|
||||||
|
* <<fake/../../saml/java/tomcat-adapter.adoc#_tomcat_adapter,Tomcat>>
|
||||||
|
* <<fake/../../saml/java/jetty-adapter.adoc#_jetty_saml_adapter,Jetty>>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
===== Apache HTTP Server
|
||||||
|
|
||||||
|
* <<fake/../../saml/mod-auth-mellon.adoc#_mod_auth_mellon,mod_auth_mellon>>
|
57
securing_apps/topics/overview/supported-protocols.adoc
Normal file
57
securing_apps/topics/overview/supported-protocols.adoc
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
[[_supported_protocols]]
|
||||||
|
|
||||||
|
=== Supported Protocols
|
||||||
|
|
||||||
|
==== OpenID Connect
|
||||||
|
|
||||||
|
link:http://openid.net/connect/[Open ID Connect] (OIDC) is an authentication protocol that is an extension of link:https://tools.ietf.org/html/rfc6749[OAuth 2.0].
|
||||||
|
While OAuth 2.0 is only a framework for building authorization protocols and is mainly incomplete, OIDC is a full-fledged authentication and authorization
|
||||||
|
protocol. OIDC also makes heavy use of the link:https://jwt.io[Json Web Token] (JWT) set of standards. These standards define an
|
||||||
|
identity token JSON format and ways to digitally sign and encrypt that data in a compact and web-friendly way.
|
||||||
|
|
||||||
|
There is really two types of use cases when using OIDC. The first is an application that asks the {{book.project.name}} server to authenticate
|
||||||
|
a user for them. After a successful login, the application will receive an _identity token_ and an _access token_. The _identity token_
|
||||||
|
contains information about the user such as username, email, and other profile information. The _access token_ is digitally signed by
|
||||||
|
the realm and contains access information (like user role mappings) that the application can use to determine what resources the user
|
||||||
|
is allowed to access on the application.
|
||||||
|
|
||||||
|
The second type of use cases is that of a client that wants to gain access to remote services. In this case, the client asks {{book.project.name}}
|
||||||
|
to obtain an _access token_ it can use to invoke on other remote services on behalf of the user. {{book.project.name}} authenticates the user
|
||||||
|
then asks the user for consent to grant access to the client requesting it. The client then receives the _access token_. This _access token_
|
||||||
|
is digitally signed by the realm. The client can make REST invocations on remote services using this _access token_. The REST service
|
||||||
|
extracts the _access token_, verifies the signature of the token, then decides based on access information within the token whether or not to process
|
||||||
|
the request.
|
||||||
|
|
||||||
|
==== SAML 2.0
|
||||||
|
|
||||||
|
link:http://saml.xml.org/saml-specifications[SAML 2.0] is a similar specification to OIDC but a lot older and more mature. It has its roots in SOAP and the plethora
|
||||||
|
of WS-* specifications so it tends to be a bit more verbose than OIDC. SAML 2.0 is primarily an authentication protocol
|
||||||
|
that works by exchanging XML documents between the authentication server and the application. XML signatures and encryption are used to verify requests and responses.
|
||||||
|
|
||||||
|
In {{book.project.name}} SAML serves two types of use cases: browser applications and REST invocations.
|
||||||
|
|
||||||
|
There is really two types of use cases when using SAML. The first is an application that asks the {{book.project.name}} server to authenticate
|
||||||
|
a user for them. After a successful login, the application will receive an XML document that contains
|
||||||
|
something called a SAML assertion that specifies various attributes about the user. This XML document is digitally signed by
|
||||||
|
the realm and contains access information (like user role mappings) that the application can use to determine what resources the user
|
||||||
|
is allowed to access on the application.
|
||||||
|
|
||||||
|
The second type of use cases is that of a client that wants to gain access to remote services. In this case, the client asks {{book.project.name}}
|
||||||
|
to obtain a SAML assertion it can use to invoke on other remote services on behalf of the user.
|
||||||
|
|
||||||
|
==== OpenID Connect vs. SAML
|
||||||
|
|
||||||
|
Choosing between OpenID Connect and SAML is not just a matter of using a newer protocol (OIDC) instead of the older more mature protocol (SAML).
|
||||||
|
|
||||||
|
In most cases {{book.project.name}} recommends using OIDC.
|
||||||
|
|
||||||
|
SAML tends to be a bit more verbose than OIDC.
|
||||||
|
|
||||||
|
Beyond verbosity of exchanged data, if you compare the specifications you'll find that OIDC was designed to work with the web while SAML was retrofitted to work on top of the web. For example, OIDC is also more suited for HTML5/JavaScript applications because it is
|
||||||
|
easier to implement on the client side than SAML. As tokens are in the JSON format,
|
||||||
|
they are easier to consume by JavaScript. You will also find several nice features that
|
||||||
|
make implementing security in your web applications easier. For example, check out the iframe trick that the specification uses to easily determine if a user is still logged in or not.
|
||||||
|
|
||||||
|
SAML has its uses though. As you see the OIDC specifications evolve you see they implement more and more features that SAML has had for years. What we often see is that people pick SAML over OIDC because of the perception that it is more mature and also because they already have existing applications that are secured with it.
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
=== What are Client Adapters?
|
||||||
|
|
||||||
|
{{book.project.name}} client adapters are libraries that makes it very easy to secure applications and services with {{book.project.name}}. We call them
|
||||||
|
adapters rather than libraries as they provide a tight integration to the underlying platform and framework. This makes our adapters easy to use and they
|
||||||
|
require less boilerplate code than what is typically required by a library.
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
==== Migration from older versions
|
||||||
|
|
||||||
|
===== Migrating to 1.9.0
|
||||||
|
|
||||||
|
====== SAML SP Client Adapter Changes
|
||||||
|
|
||||||
|
Keycloak SAML SP Client Adapter now requires a specific endpoint, `/saml` to be registered with your IdP.
|
||||||
|
The SamlFilter must also be bound to /saml in addition to any other binding it has.
|
||||||
|
This had to be done because SAML POST binding would eat the request input stream and this would be really bad for clients that relied on it.
|
||||||
|
|
||||||
|
|
107
securing_apps/topics/saml/java/assertion-api.adoc
Normal file
107
securing_apps/topics/saml/java/assertion-api.adoc
Normal file
|
@ -0,0 +1,107 @@
|
||||||
|
|
||||||
|
==== Obtaining Assertion Attributes
|
||||||
|
|
||||||
|
After a successful SAML login, your application code may want to obtain attribute values passed with the SAML assertion.
|
||||||
|
`HttpServletRequest.getUserPrincipal()` returns a `Principal` object that you can typecast into a {{book.project.name}} specific class
|
||||||
|
called `org.keycloak.adapters.saml.SamlPrincipal`.
|
||||||
|
This object allows you to look at the raw assertion and also has convenience functions to look up attribute values.
|
||||||
|
|
||||||
|
|
||||||
|
[source,java]
|
||||||
|
----
|
||||||
|
package org.keycloak.adapters.saml;
|
||||||
|
|
||||||
|
public class SamlPrincipal implements Serializable, Principal {
|
||||||
|
/**
|
||||||
|
* Get full saml assertion
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public AssertionType getAssertion() {
|
||||||
|
...
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get SAML subject sent in assertion
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public String getSamlSubject() {
|
||||||
|
...
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subject nameID format
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public String getNameIDFormat() {
|
||||||
|
...
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
...
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenience function that gets Attribute value by attribute name
|
||||||
|
*
|
||||||
|
* @param name
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public List<String> getAttributes(String name) {
|
||||||
|
...
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenience function that gets Attribute value by attribute friendly name
|
||||||
|
*
|
||||||
|
* @param friendlyName
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public List<String> getFriendlyAttributes(String friendlyName) {
|
||||||
|
...
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenience function that gets first value of an attribute by attribute name
|
||||||
|
*
|
||||||
|
* @param name
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public String getAttribute(String name) {
|
||||||
|
...
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenience function that gets first value of an attribute by attribute name
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @param friendlyName
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public String getFriendlyAttribute(String friendlyName) {
|
||||||
|
...
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get set of all assertion attribute names
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public Set<String> getAttributeNames() {
|
||||||
|
...
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get set of all assertion friendly attribute names
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public Set<String> getFriendlyNames() {
|
||||||
|
...
|
||||||
|
}
|
||||||
|
}
|
||||||
|
----
|
6
securing_apps/topics/saml/java/debugging.adoc
Normal file
6
securing_apps/topics/saml/java/debugging.adoc
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
|
||||||
|
==== Troubleshooting
|
||||||
|
|
||||||
|
The best way to troubleshoot some problems is to turn on debugging for saml in both the client adapter and the {{book.project.name}} server.
|
||||||
|
To do this turn on debugging int the `org.keycloak.saml` package to `debug` in your log4j or other logging framework.
|
||||||
|
Turning this on allows you to see the SAML requests and response documents being sent to and from the server.
|
41
securing_apps/topics/saml/java/error_handling.adoc
Normal file
41
securing_apps/topics/saml/java/error_handling.adoc
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
|
||||||
|
==== Error Handling
|
||||||
|
|
||||||
|
{{book.project.name}} has some error handling facilities for servlet based client adapters.
|
||||||
|
When an error is encountered in authentication, the client adapter will call `HttpServletResponse.sendError()`.
|
||||||
|
You can set up an `error-page` within your `web.xml` file to handle the error however you want.
|
||||||
|
The client adapter may throw 400, 401, 403, and 500 errors.
|
||||||
|
|
||||||
|
|
||||||
|
[source,xml]
|
||||||
|
----
|
||||||
|
<error-page>
|
||||||
|
<error-code>404</error-code>
|
||||||
|
<location>/ErrorHandler</location>
|
||||||
|
</error-page>
|
||||||
|
----
|
||||||
|
|
||||||
|
The client adapter also sets an `HttpServletRequest` attribute that you can retrieve.
|
||||||
|
The attribute name is `org.keycloak.adapters.spi.AuthenticationError`.
|
||||||
|
Typecast this object to: `org.keycloak.adapters.saml.SamlAuthenticationError`.
|
||||||
|
This class can tell you exactly what happened.
|
||||||
|
If this attribute is not set, then the adapter was not responsible for the error code.
|
||||||
|
|
||||||
|
|
||||||
|
[source,xml]
|
||||||
|
----
|
||||||
|
public class SamlAuthenticationError implements AuthenticationError {
|
||||||
|
public static enum Reason {
|
||||||
|
EXTRACTION_FAILURE,
|
||||||
|
INVALID_SIGNATURE,
|
||||||
|
ERROR_STATUS
|
||||||
|
}
|
||||||
|
|
||||||
|
public Reason getReason() {
|
||||||
|
return reason;
|
||||||
|
}
|
||||||
|
public StatusResponseType getStatus() {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
----
|
60
securing_apps/topics/saml/java/general-config.adoc
Normal file
60
securing_apps/topics/saml/java/general-config.adoc
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
[[_saml-general-config]]
|
||||||
|
|
||||||
|
==== General Adapter Config
|
||||||
|
|
||||||
|
Each SAML client adapter supported by {{book.project.name}} can be configured by a simple XML text file.
|
||||||
|
This is what one might look like:
|
||||||
|
|
||||||
|
[source,xml]
|
||||||
|
----
|
||||||
|
<keycloak-saml-adapter xmlns="urn:keycloak:saml:adapter"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="urn:keycloak:saml:adapter http://www.keycloak.org/schema/keycloak_saml_adapter_1_7.xsd">
|
||||||
|
<SP entityID="http://localhost:8081/sales-post-sig/"
|
||||||
|
sslPolicy="EXTERNAL"
|
||||||
|
nameIDPolicyFormat="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"
|
||||||
|
logoutPage="/logout.jsp"
|
||||||
|
forceAuthentication="false"
|
||||||
|
isPassive="false"
|
||||||
|
turnOffChangeSessionIdOnLogin="false">
|
||||||
|
<Keys>
|
||||||
|
<Key signing="true" >
|
||||||
|
<KeyStore resource="/WEB-INF/keystore.jks" password="store123">
|
||||||
|
<PrivateKey alias="http://localhost:8080/sales-post-sig/" password="test123"/>
|
||||||
|
<Certificate alias="http://localhost:8080/sales-post-sig/"/>
|
||||||
|
</KeyStore>
|
||||||
|
</Key>
|
||||||
|
</Keys>
|
||||||
|
<PrincipalNameMapping policy="FROM_NAME_ID"/>
|
||||||
|
<RoleIdentifiers>
|
||||||
|
<Attribute name="Role"/>
|
||||||
|
</RoleIdentifiers>
|
||||||
|
<IDP entityID="idp"
|
||||||
|
signaturesRequired="true">
|
||||||
|
<SingleSignOnService requestBinding="POST"
|
||||||
|
bindingUrl="http://localhost:8081/auth/realms/demo/protocol/saml"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<SingleLogoutService
|
||||||
|
requestBinding="POST"
|
||||||
|
responseBinding="POST"
|
||||||
|
postBindingUrl="http://localhost:8081/auth/realms/demo/protocol/saml"
|
||||||
|
redirectBindingUrl="http://localhost:8081/auth/realms/demo/protocol/saml"
|
||||||
|
/>
|
||||||
|
<Keys>
|
||||||
|
<Key signing="true">
|
||||||
|
<KeyStore resource="/WEB-INF/keystore.jks" password="store123">
|
||||||
|
<Certificate alias="demo"/>
|
||||||
|
</KeyStore>
|
||||||
|
</Key>
|
||||||
|
</Keys>
|
||||||
|
</IDP>
|
||||||
|
</SP>
|
||||||
|
</keycloak-saml-adapter>
|
||||||
|
----
|
||||||
|
|
||||||
|
Some of these configuration switches may be adapter specific and some are common across all adapters.
|
||||||
|
For Java adapters you can use `$\{...}` enclosure as System property replacement.
|
||||||
|
For example `$\{jboss.server.config.dir}`.
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
|
||||||
|
===== IDP Element
|
||||||
|
|
||||||
|
Everything in the IDP element describes the settings for the identity provider (authentication server) the SP is communicating with.
|
||||||
|
|
||||||
|
[source,xml]
|
||||||
|
----
|
||||||
|
<IDP entityID="idp"
|
||||||
|
signaturesRequired="true"
|
||||||
|
signatureAlgorithm="RSA_SHA1"
|
||||||
|
signatureCanonicalizationMethod="http://www.w3.org/2001/10/xml-exc-c14n#">
|
||||||
|
...
|
||||||
|
</IDP>
|
||||||
|
----
|
||||||
|
|
||||||
|
Here are the attribute config options you can specify within the `IDP` element declaration.
|
||||||
|
|
||||||
|
entityID::
|
||||||
|
This is the issuer ID of the IDP. This setting is _REQUIRED_.
|
||||||
|
|
||||||
|
signaturesRequired::
|
||||||
|
If set to `true`, the client adapter will sign every document it sends to the IDP.
|
||||||
|
Also, the client will expect that the IDP will be signing any documents sent to it.
|
||||||
|
This switch sets the default for all request and response types, but you will see later that you have some fine grain control over this.
|
||||||
|
This setting is _OPTIONAL_ and will default to `false`.
|
||||||
|
signatureAlgorithm::
|
||||||
|
This is the signature algorithm that the IDP expects signed documents to use.
|
||||||
|
Allowed values are: `RSA_SHA1`, `RSA_SHA256`, `RSA_SHA512`, and `DSA_SHA1`.
|
||||||
|
This setting is _OPTIONAL_
|
||||||
|
and defaults to `RSA_SHA256`.
|
||||||
|
signatureCanonicalizationMethod::
|
||||||
|
This is the signature canonicalization method that the IDP expects signed documents to use. This setting is _OPTIONAL_.
|
||||||
|
The default value is `http://www.w3.org/2001/10/xml-exc-c14n#` and should be good for most IDPs.
|
||||||
|
|
|
@ -0,0 +1,68 @@
|
||||||
|
[[_sp-idp-httpclient]]
|
||||||
|
|
||||||
|
===== IDP HttpClient sub element
|
||||||
|
|
||||||
|
The `HttpClient` optional sub element defines the properties of HTTP client used
|
||||||
|
for automatic obtaining of certificates containing public keys for IDP signature
|
||||||
|
verification via SAML descriptor of the IDP when
|
||||||
|
<<fake/../../../../saml/java/general-config/idp-keys_subelement.adoc#_sp-idp-keys-automatic,enabled>>.
|
||||||
|
|
||||||
|
[source,xml]
|
||||||
|
----
|
||||||
|
<HttpClient connectionPoolSize="10"
|
||||||
|
disableTrustManager="false"
|
||||||
|
allowAnyHostname="false"
|
||||||
|
clientKeystore="classpath:keystore.jks"
|
||||||
|
clientKeystorePassword="pwd"
|
||||||
|
truststore="classpath:truststore.jks"
|
||||||
|
truststorePassword="pwd"
|
||||||
|
proxyUrl="http://proxy/" />
|
||||||
|
----
|
||||||
|
|
||||||
|
connectionPoolSize::
|
||||||
|
Adapters will make separate HTTP invocations to the {{book.project.name}} server to turn an access code into an access token.
|
||||||
|
This config option defines how many connections to the {{book.project.name}} server should be pooled.
|
||||||
|
This is _OPTIONAL_.
|
||||||
|
The default value is `10`.
|
||||||
|
|
||||||
|
disableTrustManager::
|
||||||
|
If the {{book.project.name}} server requires HTTPS and this config option is set to `true` you do not have to specify a truststore.
|
||||||
|
This setting should only be used during development and *never* in production as it will disable verification of SSL certificates.
|
||||||
|
This is _OPTIONAL_.
|
||||||
|
The default value is `false`.
|
||||||
|
|
||||||
|
allowAnyHostname::
|
||||||
|
If the {{book.project.name}} server requires HTTPS and this config option is set to `true`
|
||||||
|
the {{book.project.name}} server's certificate is validated via the truststore,
|
||||||
|
but host name validation is not done.
|
||||||
|
This setting should only be used during development and *never* in production
|
||||||
|
as it will partly disable verification of SSL certificates.
|
||||||
|
This seting may be useful in test environments. This is _OPTIONAL_.
|
||||||
|
The default value is `false`.
|
||||||
|
|
||||||
|
truststore::
|
||||||
|
The value is the file path to a keystore file.
|
||||||
|
If you prefix the path with `classpath:`, then the truststore will be obtained from the deployment's classpath instead.
|
||||||
|
Used for outgoing HTTPS communications to the {{book.project.name}} server.
|
||||||
|
Client making HTTPS requests need a way to verify the host of the server they are talking to.
|
||||||
|
This is what the trustore does.
|
||||||
|
The keystore contains one or more trusted host certificates or certificate authorities.
|
||||||
|
You can create this truststore by extracting the public certificate of the {{book.project.name}} server's SSL keystore.
|
||||||
|
This is _REQUIRED_ unless `disableTrustManager` is `true`.
|
||||||
|
|
||||||
|
truststorePassword::
|
||||||
|
Password for the truststore keystore.
|
||||||
|
This is _REQUIRED_ if `truststore` is set and the truststore requires a password.
|
||||||
|
|
||||||
|
clientKeystore::
|
||||||
|
This is the file path to a keystore file.
|
||||||
|
This keystore contains client certificate for two-way SSL when the adapter makes HTTPS requests to the {{book.project.name}} server.
|
||||||
|
This is _OPTIONAL_.
|
||||||
|
|
||||||
|
clientKeystorePassword::
|
||||||
|
Password for the client keystore and for the client's key.
|
||||||
|
This is _REQUIRED_ if `clientKeystore` is set.
|
||||||
|
|
||||||
|
proxyUrl::
|
||||||
|
URL to HTTP proxy to use for HTTP connections.
|
||||||
|
This is _OPTIONAL_.
|
|
@ -0,0 +1,45 @@
|
||||||
|
[[_sp-idp-keys]]
|
||||||
|
|
||||||
|
===== IDP Keys sub element
|
||||||
|
|
||||||
|
The Keys sub element of IDP is only used to define the certificate or public key to use to verify documents signed by the IDP.
|
||||||
|
It is defined in the same way as the <<fake/../../../../saml/java/general-config/sp-keys.adoc#_saml-sp-keys,SP's Keys element>>.
|
||||||
|
But again, you only have to define one certificate or public key reference. Note that, if both IDP and SP are realized by
|
||||||
|
{{book.project.name}} server and adapter, respectively, there is no need to specify the keys for signature validation, see below.
|
||||||
|
|
||||||
|
[[_sp-idp-keys-automatic]]
|
||||||
|
It is possible to configure SP to obtain public keys for IDP signature validation
|
||||||
|
from published certificates automatically, provided both SP and IDP are
|
||||||
|
implemented by {{book.project.name}}.
|
||||||
|
This is done by removing all declarations of signature validation keys in Keys
|
||||||
|
sub element. If the Keys sub element would then remain empty, it can be omitted
|
||||||
|
completely. The keys are then automatically obtained by SP from SAML descriptor,
|
||||||
|
location of which is derived from SAML endpoint URL specified in the
|
||||||
|
<<fake/../../../../saml/java/general-config/idp_singlesignonservice_subelement.adoc#_sp-idp-singlesignonservice,IDP SingleSignOnService sub element>>.
|
||||||
|
Settings of the HTTP client that is used for SAML descriptor retrieval usually
|
||||||
|
needs no additional configuration, however it can be configured in the
|
||||||
|
<<fake/../../../../saml/java/general-config/idp_httpclient_subelement.adoc#_sp-idp-httpclient,IDP HttpClient sub element>>.
|
||||||
|
|
||||||
|
It is also possible to specify multiple keys for signature verification. This is done by declaring multiple Key elements
|
||||||
|
within Keys sub element that have `signing` attribute set to `true`.
|
||||||
|
This is useful for example in situation when the IDP signing keys are rotated: There is
|
||||||
|
usually a transition period when new SAML protocol messages and assertions are signed
|
||||||
|
with the new key but those signed by previous key should still be accepted.
|
||||||
|
|
||||||
|
It is not possible to configure {{book.project.name}} to both obtain the keys
|
||||||
|
for signature verification automatically and define additional static signature
|
||||||
|
verification keys.
|
||||||
|
|
||||||
|
[source,xml]
|
||||||
|
----
|
||||||
|
<IDP entityID="idp">
|
||||||
|
...
|
||||||
|
<Keys>
|
||||||
|
<Key signing="true">
|
||||||
|
<KeyStore resource="/WEB-INF/keystore.jks" password="store123">
|
||||||
|
<Certificate alias="demo"/>
|
||||||
|
</KeyStore>
|
||||||
|
</Key>
|
||||||
|
</Keys>
|
||||||
|
</IDP>
|
||||||
|
----
|
|
@ -0,0 +1,47 @@
|
||||||
|
|
||||||
|
===== IDP SingleLogoutService sub element
|
||||||
|
|
||||||
|
The `SingleLogoutService` sub element defines the logout SAML endpoint of the IDP. The client adapter will send requests
|
||||||
|
to the IDP formatted via the settings within this element when it wants to logout.
|
||||||
|
|
||||||
|
[source,xml]
|
||||||
|
----
|
||||||
|
<SingleLogoutService validateRequestSignature="true"
|
||||||
|
validateResponseSignature="true"
|
||||||
|
signRequest="true"
|
||||||
|
signResponse="true"
|
||||||
|
requestBinding="redirect"
|
||||||
|
responseBinding="post"
|
||||||
|
postBindingUrl="posturl"
|
||||||
|
redirectBindingUrl="redirecturl">
|
||||||
|
----
|
||||||
|
|
||||||
|
signRequest::
|
||||||
|
Should the client sign logout requests it makes to the IDP? This setting is _OPTIONAL_.
|
||||||
|
Defaults to whatever the IDP `signaturesRequired` element value is.
|
||||||
|
|
||||||
|
signResponse::
|
||||||
|
Should the client sign logout responses it sends to the IDP requests? This setting is _OPTIONAL_.
|
||||||
|
Defaults to whatever the IDP `signaturesRequired` element value is.
|
||||||
|
|
||||||
|
validateRequestSignature::
|
||||||
|
Should the client expect signed logout request documents from the IDP? This setting is _OPTIONAL_. Defaults to whatever the IDP `signaturesRequired` element value is.
|
||||||
|
|
||||||
|
validateResponseSignature::
|
||||||
|
Should the client expect signed logout response documents from the IDP? This setting is _OPTIONAL_. Defaults to whatever the IDP `signaturesRequired` element value is.
|
||||||
|
|
||||||
|
requestBinding::
|
||||||
|
This is the SAML binding type used for communicating SAML requests to the IDP. This setting is _OPTIONAL_.
|
||||||
|
The default value is `POST`, but you can set it to REDIRECT as well.
|
||||||
|
|
||||||
|
responseBinding::
|
||||||
|
This is the SAML binding type used for communicating SAML responses to the IDP. The values of this can be `POST` or `REDIRECT`. This setting is _OPTIONAL_.
|
||||||
|
The default value is `POST`, but you can set it to `REDIRECT` as well.
|
||||||
|
|
||||||
|
postBindingUrl::
|
||||||
|
This is the URL for the IDP's logout service when using the POST binding. This setting is _REQUIRED_ if using the `POST` binding.
|
||||||
|
|
||||||
|
redirectBindingUrl::
|
||||||
|
This is the URL for the IDP's logout service when using the REDIRECT binding. This setting is _REQUIRED_ if using the REDIRECT binding.
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
[[_sp-idp-singlesignonservice]]
|
||||||
|
|
||||||
|
===== IDP SingleSignOnService sub element
|
||||||
|
|
||||||
|
The `SingleSignOnService` sub element defines the login SAML endpoint of the IDP.
|
||||||
|
The client adapter will send requests
|
||||||
|
to the IDP formatted via the settings within this element when it wants to login.
|
||||||
|
|
||||||
|
[source,xml]
|
||||||
|
----
|
||||||
|
|
||||||
|
<SingleSignOnService signRequest="true"
|
||||||
|
validateResponseSignature="true"
|
||||||
|
requestBinding="post"
|
||||||
|
bindingUrl="url"/>
|
||||||
|
----
|
||||||
|
|
||||||
|
Here are the config attributes you can define on this element:
|
||||||
|
|
||||||
|
signRequest::
|
||||||
|
Should the client sign authn requests? This setting is _OPTIONAL_.
|
||||||
|
Defaults to whatever the IDP `signaturesRequired` element value is.
|
||||||
|
|
||||||
|
validateResponseSignature::
|
||||||
|
Should the client expect the IDP to sign the assertion response document sent back from an auhtn request?
|
||||||
|
This setting _OPTIONAL_. Defaults to whatever the IDP `signaturesRequired` element value is.
|
||||||
|
|
||||||
|
requestBinding::
|
||||||
|
This is the SAML binding type used for communicating with the IDP. This setting is _OPTIONAL_.
|
||||||
|
The default value is `POST`, but you can set it to `REDIRECT` as well.
|
||||||
|
|
||||||
|
responseBinding::
|
||||||
|
SAML allows the client to request what binding type it wants authn responses to use.
|
||||||
|
The values of this can be `POST` or `REDIRECT`. This setting is _OPTIONAL_.
|
||||||
|
The default is that the client will not request a specific binding type for responses.
|
||||||
|
|
||||||
|
bindingUrl::
|
||||||
|
This is the URL for the IDP login service that the client will send requests to. This setting is _REQUIRED_.
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
|
||||||
|
===== RoleIdentifiers Element
|
||||||
|
|
||||||
|
The `RoleIdentifiers` element defines what SAML attributes within the assertion received from the user should be used
|
||||||
|
as role identifiers within the Java EE Security Context for the user.
|
||||||
|
|
||||||
|
[source,xml]
|
||||||
|
----
|
||||||
|
|
||||||
|
<RoleIdentifiers>
|
||||||
|
<Attribute name="Role"/>
|
||||||
|
<Attribute name="member"/>
|
||||||
|
<Attribute name="memberOf"/>
|
||||||
|
</RoleIdentifiers>
|
||||||
|
----
|
||||||
|
|
||||||
|
By default `Role` attribute values are converted to Java EE roles.
|
||||||
|
Some IdPs send roles using a `member` or `memberOf` attribute assertion.
|
||||||
|
You can define one or more `Attribute` elements to specify which SAML attributes must be converted into roles.
|
||||||
|
|
||||||
|
|
26
securing_apps/topics/saml/java/general-config/sp-keys.adoc
Normal file
26
securing_apps/topics/saml/java/general-config/sp-keys.adoc
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
[[_saml-sp-keys]]
|
||||||
|
|
||||||
|
===== Service Provider Keys and Key Elements
|
||||||
|
|
||||||
|
If the IdP requires that the client application (or SP) sign all of its requests and/or if the IdP will encrypt assertions, you must define the keys used to do this.
|
||||||
|
For client-signed documents you must define both the private and public key or certificate that is used to sign documents.
|
||||||
|
For encryption, you only have to define the private key that is used to decrypt it.
|
||||||
|
|
||||||
|
There are two ways to describe your keys.
|
||||||
|
They can be stored within a Java KeyStore or you can copy/paste the keys directly within `keycloak-saml.xml` in the PEM format.
|
||||||
|
|
||||||
|
[source,xml]
|
||||||
|
----
|
||||||
|
|
||||||
|
<Keys>
|
||||||
|
<Key signing="true" >
|
||||||
|
...
|
||||||
|
</Key>
|
||||||
|
</Keys>
|
||||||
|
----
|
||||||
|
|
||||||
|
The `Key` element has two optional attributes `signing` and `encryption`.
|
||||||
|
When set to true these tell the adapter what the key will be used for.
|
||||||
|
If both attributes are set to true, then the key will be used for both signing documents and decrypting encrypted assertions.
|
||||||
|
You must set at least one of these attributes to true.
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
|
||||||
|
====== Key PEMS
|
||||||
|
|
||||||
|
Within the `Key` element you declare your keys and certificates directly using the sub elements
|
||||||
|
`PrivateKeyPem`, `PublicKeyPem`, and `CertificatePem`.
|
||||||
|
The values contained in these elements must conform to the PEM key format.
|
||||||
|
You usually use this option if you are generating keys using `openssl` or similar command line tool.
|
||||||
|
|
||||||
|
[source,xml]
|
||||||
|
----
|
||||||
|
<Keys>
|
||||||
|
<Key signing="true">
|
||||||
|
<PrivateKeyPem>
|
||||||
|
2341251234AB31234==231BB998311222423522334
|
||||||
|
</PrivateKeyPem>
|
||||||
|
<CertificatePem>
|
||||||
|
211111341251234AB31234==231BB998311222423522334
|
||||||
|
</CertificatePem>
|
||||||
|
</Key>
|
||||||
|
</Keys>
|
||||||
|
----
|
|
@ -0,0 +1,37 @@
|
||||||
|
[[_saml-keystore]]
|
||||||
|
|
||||||
|
====== KeyStore element
|
||||||
|
|
||||||
|
Within the `Key` element you can load your keys and certificates from a Java Keystore. This is declared within
|
||||||
|
a `KeyStore` element.
|
||||||
|
|
||||||
|
[source,xml]
|
||||||
|
----
|
||||||
|
|
||||||
|
<Keys>
|
||||||
|
<Key signing="true" >
|
||||||
|
<KeyStore resource="/WEB-INF/keystore.jks" password="store123">
|
||||||
|
<PrivateKey alias="myPrivate" password="test123"/>
|
||||||
|
<Certificate alias="myCertAlias"/>
|
||||||
|
</KeyStore>
|
||||||
|
</Key>
|
||||||
|
</Keys>
|
||||||
|
----
|
||||||
|
|
||||||
|
Here are the XML config attributes that are defined with the `KeyStore` element.
|
||||||
|
|
||||||
|
file::
|
||||||
|
File path to the key store. This option is _OPTIONAL_. The file or resource attribute must be set.
|
||||||
|
|
||||||
|
resource::
|
||||||
|
WAR resource path to the KeyStore.
|
||||||
|
This is a path used in method call to ServletContext.getResourceAsStream(). This option is _OPTIONAL_. The file or resource attribute must be set.
|
||||||
|
|
||||||
|
password::
|
||||||
|
The password of the KeyStore. This option is _REQUIRED_.
|
||||||
|
|
||||||
|
If you are defining keys that the SP will use to sign document, you must also specify references to your private keys
|
||||||
|
and certificates within the Java KeyStore.
|
||||||
|
The `PrivateKey` and `Certificate` elements in the above example define an `alias` that points to the key or cert
|
||||||
|
within the keystore. Keystores require an additional password to access private keys.
|
||||||
|
In the `PrivateKey` element you must define this password within a `password` attribute.
|
|
@ -0,0 +1,51 @@
|
||||||
|
|
||||||
|
===== SP Element
|
||||||
|
|
||||||
|
Here is the explanation of the SP element attributes:
|
||||||
|
|
||||||
|
[source,xml]
|
||||||
|
----
|
||||||
|
|
||||||
|
<SP entityID="sp"
|
||||||
|
sslPolicy="ssl"
|
||||||
|
nameIDPolicyFormat="format"
|
||||||
|
forceAuthentication="true"
|
||||||
|
isPassive="false">
|
||||||
|
...
|
||||||
|
</SP>
|
||||||
|
----
|
||||||
|
entityID::
|
||||||
|
This is the identifier for this client.
|
||||||
|
The IdP needs this value to determine who the client is that is communicating with it. This setting is _REQUIRED_.
|
||||||
|
|
||||||
|
sslPolicy::
|
||||||
|
This is the SSL policy the adapter will enforce.
|
||||||
|
Valid values are: `ALL`, `EXTERNAL`, and `NONE`.
|
||||||
|
For `ALL`, all requests must come in via HTTPS.
|
||||||
|
For `EXTERNAL`, only non-private IP addresses must come over the wire via HTTPS.
|
||||||
|
For `NONE`, no requests are required to come over via HTTPS.
|
||||||
|
This setting is _OPTIONAL_. Default value is `EXTERNAL`.
|
||||||
|
|
||||||
|
nameIDPolicyFormat::
|
||||||
|
SAML clients can request a specific NameID Subject format.
|
||||||
|
Fill in this value if you want a specific format.
|
||||||
|
It must be a standard SAML format identifier: `urn:oasis:names:tc:SAML:2.0:nameid-format:transient`.
|
||||||
|
This setting is _OPTIONAL_.
|
||||||
|
By default, no special format is requested.
|
||||||
|
|
||||||
|
forceAuthentication::
|
||||||
|
SAML clients can request that a user is re-authenticated even if they are already logged in at the IdP.
|
||||||
|
Set this to `true` to enable. This setting is _OPTIONAL_.
|
||||||
|
Default value is `false`.
|
||||||
|
|
||||||
|
isPassive::
|
||||||
|
SAML clients can request that a user is never asked to authenticate even if they are not logged in at the IdP.
|
||||||
|
Set this to `true` if you want this.
|
||||||
|
Do not use together with `forceAuthentication` as they are opposite. This setting is _OPTIONAL_.
|
||||||
|
Default value is `false`.
|
||||||
|
|
||||||
|
turnOffChangeSessionIdOnLogin::
|
||||||
|
The session ID is changed by default on a successful login on some platforms to plug a security attack vector.
|
||||||
|
Change this to `true` to disable this. It is recommended you do not turn it off.
|
||||||
|
Default value is `false`.
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
|
||||||
|
===== SP PrincipalNameMapping element
|
||||||
|
|
||||||
|
This element is optional.
|
||||||
|
When creating a Java Principal object that you obtain from methods like `HttpServletRequest.getUserPrincipal()`, you can
|
||||||
|
define what name that is returned by the `Principal.getName()` method.
|
||||||
|
|
||||||
|
[source,xml]
|
||||||
|
----
|
||||||
|
<SP ...>
|
||||||
|
<PrincipalNameMapping policy="FROM_NAME_ID"/>
|
||||||
|
</SP>
|
||||||
|
|
||||||
|
<SP ...>
|
||||||
|
<PrincipalNameMapping policy="FROM_ATTRIBUTE" attribute="email" />
|
||||||
|
</SP>
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
|
||||||
|
The `policy` attribute defines the policy used to populate this value.
|
||||||
|
The possible values for this attribute are:
|
||||||
|
|
||||||
|
FROM_NAME_ID::
|
||||||
|
This policy just uses whatever the SAML subject value is. This is the default setting
|
||||||
|
FROM_ATTRIBUTE::
|
||||||
|
This will pull the value from one of the attributes declared in the SAML assertion received from the server.
|
||||||
|
You'll need to specify the name of the SAML assertion attribute to use within the `attribute` XML attribute.
|
6
securing_apps/topics/saml/java/idp-registration.adoc
Normal file
6
securing_apps/topics/saml/java/idp-registration.adoc
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
|
||||||
|
==== Registering with an Identity Provider
|
||||||
|
|
||||||
|
For each servlet-based adapter, the endpoint you register for the assert consumer service URL and and single logout service
|
||||||
|
must be the base URL of your servlet application with `/saml` appended to it, that is, `$$https://example.com/contextPath/saml$$`.
|
||||||
|
|
4
securing_apps/topics/saml/java/java-adapters.adoc
Normal file
4
securing_apps/topics/saml/java/java-adapters.adoc
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
|
||||||
|
=== Java Adapters
|
||||||
|
|
||||||
|
{{book.project.name}} comes with a range of different adapters for Java application. Selecting the correct adapter depends on the target platform.
|
|
@ -0,0 +1,136 @@
|
||||||
|
|
||||||
|
[[_saml-jboss-adapter-installation]]
|
||||||
|
===== Adapter Installation
|
||||||
|
|
||||||
|
Each adapter is a separate download on the {{book.project.name}} download site.
|
||||||
|
|
||||||
|
{% if book.community %}
|
||||||
|
Install on Wildfly 9 or 10, or JBoss EAP 7:
|
||||||
|
|
||||||
|
[source]
|
||||||
|
----
|
||||||
|
|
||||||
|
$ cd $WILDFLY_HOME
|
||||||
|
$ unzip keycloak-saml-wildfly-adapter-dist.zip
|
||||||
|
----
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
|
||||||
|
{% if book.community %}
|
||||||
|
Install on JBoss EAP 6.x:
|
||||||
|
[source]
|
||||||
|
----
|
||||||
|
|
||||||
|
$ cd $JBOSS_HOME
|
||||||
|
$ unzip keycloak-saml-eap6-adapter-dist.zip
|
||||||
|
----
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if book.product %}
|
||||||
|
Install on JBoss EAP 6.x:
|
||||||
|
[source]
|
||||||
|
----
|
||||||
|
|
||||||
|
$ cd $JBOSS_HOME
|
||||||
|
$ unzip rh-sso-saml-eap6-adapter.zip
|
||||||
|
----
|
||||||
|
|
||||||
|
Install on JBoss EAP 7.x:
|
||||||
|
[source]
|
||||||
|
----
|
||||||
|
|
||||||
|
$ cd $JBOSS_HOME
|
||||||
|
$ unzip rh-sso-saml-eap7-adapter.zip
|
||||||
|
----
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
|
||||||
|
These zip files create new JBoss Modules specific to the Wildfly/JBoss EAP SAML Adapter within your Wildfly or JBoss EAP distro.
|
||||||
|
|
||||||
|
After adding the modules, you must then enable the {{book.project.name}} SAML Subsystem within your app server's server configuration: `domain.xml` or `standalone.xml`.
|
||||||
|
|
||||||
|
There is a CLI script that will help you modify your server configuration.
|
||||||
|
Start the server and run the script from the server's bin directory:
|
||||||
|
|
||||||
|
[source]
|
||||||
|
----
|
||||||
|
|
||||||
|
$ cd $JBOSS_HOME/bin
|
||||||
|
$ jboss-cli.sh -c --file=adapter-install-saml.cli
|
||||||
|
----
|
||||||
|
The script will add the extension, subsystem, and optional security-domain as described below.
|
||||||
|
|
||||||
|
[source,xml]
|
||||||
|
----
|
||||||
|
<server xmlns="urn:jboss:domain:1.4">
|
||||||
|
|
||||||
|
<extensions>
|
||||||
|
<extension module="org.keycloak.keycloak-saml-adapter-subsystem"/>
|
||||||
|
...
|
||||||
|
</extensions>
|
||||||
|
|
||||||
|
<profile>
|
||||||
|
<subsystem xmlns="urn:jboss:domain:keycloak-saml:1.1"/>
|
||||||
|
...
|
||||||
|
</profile>
|
||||||
|
----
|
||||||
|
|
||||||
|
The `keycloak` security domain should be used with EJBs and other components when you need the security context created
|
||||||
|
in the secured web tier to be propagated to the EJBs (other EE component) you are invoking.
|
||||||
|
Otherwise this configuration is optional.
|
||||||
|
|
||||||
|
[source,xml]
|
||||||
|
----
|
||||||
|
|
||||||
|
<server xmlns="urn:jboss:domain:1.4">
|
||||||
|
<subsystem xmlns="urn:jboss:domain:security:1.2">
|
||||||
|
<security-domains>
|
||||||
|
...
|
||||||
|
<security-domain name="keycloak">
|
||||||
|
<authentication>
|
||||||
|
<login-module code="org.keycloak.adapters.jboss.KeycloakLoginModule"
|
||||||
|
flag="required"/>
|
||||||
|
</authentication>
|
||||||
|
</security-domain>
|
||||||
|
</security-domains>
|
||||||
|
----
|
||||||
|
|
||||||
|
For example, if you have a JAX-RS service that is an EJB within your WEB-INF/classes directory,
|
||||||
|
you'll want to annotate it with the `@SecurityDomain` annotation as follows:
|
||||||
|
|
||||||
|
[source,xml]
|
||||||
|
----
|
||||||
|
|
||||||
|
import org.jboss.ejb3.annotation.SecurityDomain;
|
||||||
|
import org.jboss.resteasy.annotations.cache.NoCache;
|
||||||
|
|
||||||
|
import javax.annotation.security.RolesAllowed;
|
||||||
|
import javax.ejb.EJB;
|
||||||
|
import javax.ejb.Stateless;
|
||||||
|
import javax.ws.rs.GET;
|
||||||
|
import javax.ws.rs.Path;
|
||||||
|
import javax.ws.rs.Produces;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Path("customers")
|
||||||
|
@Stateless
|
||||||
|
@SecurityDomain("keycloak")
|
||||||
|
public class CustomerService {
|
||||||
|
|
||||||
|
@EJB
|
||||||
|
CustomerDB db;
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Produces("application/json")
|
||||||
|
@NoCache
|
||||||
|
@RolesAllowed("db_user")
|
||||||
|
public List<String> getCustomers() {
|
||||||
|
return db.getCustomers();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
|
We hope to improve our integration in the future so that you don't have to specify the
|
||||||
|
`@SecurityDomain` annotation when you want to propagate a keycloak security context to the EJB tier.
|
||||||
|
|
|
@ -0,0 +1,64 @@
|
||||||
|
|
||||||
|
===== Per WAR Configuration
|
||||||
|
|
||||||
|
This section describes how to secure a WAR directly by adding config and editing files within your WAR package.
|
||||||
|
|
||||||
|
The first thing you must do is create a `keycloak-saml.xml` adapter config file within the `WEB-INF` directory of your WAR.
|
||||||
|
The format of this config file is described in the <<fake/../../../../saml/java/general-config.adoc#_saml-general-config,General Adapter Config>> section.
|
||||||
|
|
||||||
|
Next you must set the `auth-method` to `KEYCLOAK-SAML` in `web.xml`.
|
||||||
|
You also have to use standard servlet security to specify role-base constraints on your URLs.
|
||||||
|
Here's an example _web.xml_ file:
|
||||||
|
|
||||||
|
[source,xml]
|
||||||
|
----
|
||||||
|
|
||||||
|
<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">
|
||||||
|
|
||||||
|
<module-name>customer-portal</module-name>
|
||||||
|
|
||||||
|
<security-constraint>
|
||||||
|
<web-resource-collection>
|
||||||
|
<web-resource-name>Admins</web-resource-name>
|
||||||
|
<url-pattern>/admin/*</url-pattern>
|
||||||
|
</web-resource-collection>
|
||||||
|
<auth-constraint>
|
||||||
|
<role-name>admin</role-name>
|
||||||
|
</auth-constraint>
|
||||||
|
<user-data-constraint>
|
||||||
|
<transport-guarantee>CONFIDENTIAL</transport-guarantee>
|
||||||
|
</user-data-constraint>
|
||||||
|
</security-constraint>
|
||||||
|
<security-constraint>
|
||||||
|
<web-resource-collection>
|
||||||
|
<web-resource-name>Customers</web-resource-name>
|
||||||
|
<url-pattern>/customers/*</url-pattern>
|
||||||
|
</web-resource-collection>
|
||||||
|
<auth-constraint>
|
||||||
|
<role-name>user</role-name>
|
||||||
|
</auth-constraint>
|
||||||
|
<user-data-constraint>
|
||||||
|
<transport-guarantee>CONFIDENTIAL</transport-guarantee>
|
||||||
|
</user-data-constraint>
|
||||||
|
</security-constraint>
|
||||||
|
|
||||||
|
<login-config>
|
||||||
|
<auth-method>KEYCLOAK-SAML</auth-method>
|
||||||
|
<realm-name>this is ignored currently</realm-name>
|
||||||
|
</login-config>
|
||||||
|
|
||||||
|
<security-role>
|
||||||
|
<role-name>admin</role-name>
|
||||||
|
</security-role>
|
||||||
|
<security-role>
|
||||||
|
<role-name>user</role-name>
|
||||||
|
</security-role>
|
||||||
|
</web-app>
|
||||||
|
----
|
||||||
|
|
||||||
|
All standard servlet settings except the `auth-method` setting.
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,82 @@
|
||||||
|
|
||||||
|
===== Securing WARs via {{book.project.name}} SAML Subsystem
|
||||||
|
|
||||||
|
You do not have to crack open a WAR to secure it with {{book.project.name}}.
|
||||||
|
Alternatively, you can externally secure it via the {{book.project.name}} SAML Adapter Subsystem.
|
||||||
|
While you don't have to specify KEYCLOAK-SAML as an `auth-method`, you still have to define the `security-constraints` in `web.xml`.
|
||||||
|
You do not, however, have to create a `WEB-INF/keycloak-saml.xml` file.
|
||||||
|
This metadata is instead defined within the XML in your server's `domain.xml` or `standalone.xml` subsystem configuration section.
|
||||||
|
|
||||||
|
[source,xml]
|
||||||
|
----
|
||||||
|
|
||||||
|
<extensions>
|
||||||
|
<extension module="org.keycloak.keycloak-saml-adapter-subsystem"/>
|
||||||
|
</extensions>
|
||||||
|
|
||||||
|
<profile>
|
||||||
|
<subsystem xmlns="urn:jboss:domain:keycloak-saml:1.1">
|
||||||
|
<secure-deployment name="WAR MODULE NAME.war">
|
||||||
|
<SP entityID="APPLICATION URL">
|
||||||
|
...
|
||||||
|
</SP>
|
||||||
|
</secure-deployment>
|
||||||
|
</subsystem>
|
||||||
|
</profile>
|
||||||
|
----
|
||||||
|
|
||||||
|
The `secure-deployment` `name` attribute identifies the WAR you want to secure.
|
||||||
|
Its value is the `module-name` defined in `web.xml` with `.war` appended.
|
||||||
|
The rest of the configuration uses the same XML syntax as `keycloak-saml.xml` configuration defined in <<fake/../../../../saml/java/general-config.adoc#_saml-general-config,General Adapter Config>>.
|
||||||
|
|
||||||
|
An example configuration:
|
||||||
|
|
||||||
|
[source,xml]
|
||||||
|
----
|
||||||
|
|
||||||
|
<subsystem xmlns="urn:jboss:domain:keycloak-saml:1.1">
|
||||||
|
<secure-deployment name="saml-post-encryption.war">
|
||||||
|
<SP entityID="http://localhost:8080/sales-post-enc/"
|
||||||
|
sslPolicy="EXTERNAL"
|
||||||
|
nameIDPolicyFormat="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"
|
||||||
|
logoutPage="/logout.jsp"
|
||||||
|
forceAuthentication="false">
|
||||||
|
<Keys>
|
||||||
|
<Key signing="true" encryption="true">
|
||||||
|
<KeyStore resource="/WEB-INF/keystore.jks" password="store123">
|
||||||
|
<PrivateKey alias="http://localhost:8080/sales-post-enc/" password="test123"/>
|
||||||
|
<Certificate alias="http://localhost:8080/sales-post-enc/"/>
|
||||||
|
</KeyStore>
|
||||||
|
</Key>
|
||||||
|
</Keys>
|
||||||
|
<PrincipalNameMapping policy="FROM_NAME_ID"/>
|
||||||
|
<RoleIdentifiers>
|
||||||
|
<Attribute name="Role"/>
|
||||||
|
</RoleIdentifiers>
|
||||||
|
<IDP entityID="idp">
|
||||||
|
<SingleSignOnService signRequest="true"
|
||||||
|
validateResponseSignature="true"
|
||||||
|
requestBinding="POST"
|
||||||
|
bindingUrl="http://localhost:8080/auth/realms/saml-demo/protocol/saml"/>
|
||||||
|
|
||||||
|
<SingleLogoutService
|
||||||
|
validateRequestSignature="true"
|
||||||
|
validateResponseSignature="true"
|
||||||
|
signRequest="true"
|
||||||
|
signResponse="true"
|
||||||
|
requestBinding="POST"
|
||||||
|
responseBinding="POST"
|
||||||
|
postBindingUrl="http://localhost:8080/auth/realms/saml-demo/protocol/saml"
|
||||||
|
redirectBindingUrl="http://localhost:8080/auth/realms/saml-demo/protocol/saml"/>
|
||||||
|
<Keys>
|
||||||
|
<Key signing="true" >
|
||||||
|
<KeyStore resource="/WEB-INF/keystore.jks" password="store123">
|
||||||
|
<Certificate alias="saml-demo"/>
|
||||||
|
</KeyStore>
|
||||||
|
</Key>
|
||||||
|
</Keys>
|
||||||
|
</IDP>
|
||||||
|
</SP>
|
||||||
|
</secure-deployment>
|
||||||
|
</subsystem>
|
||||||
|
----
|
7
securing_apps/topics/saml/java/jetty-adapter.adoc
Normal file
7
securing_apps/topics/saml/java/jetty-adapter.adoc
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
[[_jetty_saml_adapter]]
|
||||||
|
|
||||||
|
==== Jetty SAML Adapters
|
||||||
|
|
||||||
|
To be able to secure WAR apps deployed on Jetty you must install the {{book.project.name}} Jetty 9.x or 8.x SAML adapter into your Jetty installation.
|
||||||
|
You then have to provide some extra configuration in each WAR you deploy to Jetty.
|
||||||
|
Let's go over these steps.
|
|
@ -0,0 +1,32 @@
|
||||||
|
|
||||||
|
===== Jetty 8 Adapter Installation
|
||||||
|
|
||||||
|
Keycloak has a separate SAML adapter for Jetty 8.1.x that you will have to install into your Jetty installation.
|
||||||
|
You then have to provide some extra configuration in each WAR you deploy to Jetty.
|
||||||
|
Let's go over these steps.
|
||||||
|
|
||||||
|
Adapters are no longer included with the appliance or war distribution.Each adapter is a separate download on the Keycloak download site.
|
||||||
|
They are also available as a maven artifact.
|
||||||
|
|
||||||
|
You must unzip the Jetty 8.1.x distro into Jetty 8.1.x's root directory.
|
||||||
|
Including adapter's jars within your WEB-INF/lib directory will not work!
|
||||||
|
|
||||||
|
[source]
|
||||||
|
----
|
||||||
|
$ cd $JETTY_HOME
|
||||||
|
$ unzip keycloak-saml-jetty81-adapter-dist.zip
|
||||||
|
----
|
||||||
|
Next, you will have to enable the keycloak option.
|
||||||
|
Edit start.ini and add keycloak to the options
|
||||||
|
|
||||||
|
[source]
|
||||||
|
----
|
||||||
|
#===========================================================
|
||||||
|
# Start classpath OPTIONS.
|
||||||
|
# These control what classes are on the classpath
|
||||||
|
# for a full listing do
|
||||||
|
# java -jar start.jar --list-options
|
||||||
|
#-----------------------------------------------------------
|
||||||
|
OPTIONS=Server,jsp,jmx,resources,websocket,ext,plus,annotations,keycloak
|
||||||
|
----
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
|
||||||
|
===== Jetty 8 Per WAR Configuration
|
||||||
|
|
||||||
|
Enabling Keycloak for your WARs is the same as the Jetty 9.x adapter.
|
||||||
|
See <<fake/../../../../saml/java/jetty-adapter/jetty9_per_war_config.adoc#_saml-jetty9-per-war, Jetty 9 Per War Configuration>>
|
|
@ -0,0 +1,27 @@
|
||||||
|
[[_jetty9_saml_adapter_installation]]
|
||||||
|
|
||||||
|
===== Jetty 9 Adapter Installation
|
||||||
|
|
||||||
|
Keycloak has a separate SAML adapter for Jetty 9.x.
|
||||||
|
You then have to provide some extra configuration in each WAR you deploy to Jetty.
|
||||||
|
Let's go over these steps.
|
||||||
|
|
||||||
|
Adapters are no longer included with the appliance or war distribution.Each adapter is a separate download on the Keycloak download site.
|
||||||
|
They are also available as a maven artifact.
|
||||||
|
|
||||||
|
You must unzip the Jetty 9.x distro into Jetty 9.x's root directory.
|
||||||
|
Including adapter's jars within your WEB-INF/lib directory will not work!
|
||||||
|
|
||||||
|
[source]
|
||||||
|
----
|
||||||
|
$ cd $JETTY_HOME
|
||||||
|
$ unzip keycloak-saml-jetty92-adapter-dist.zip
|
||||||
|
----
|
||||||
|
Next, you will have to enable the keycloak module for your jetty.base.
|
||||||
|
|
||||||
|
[source]
|
||||||
|
----
|
||||||
|
$ cd your-base
|
||||||
|
$ java -jar $JETTY_HOME/start.jar --add-to-startd=keycloak
|
||||||
|
----
|
||||||
|
|
|
@ -0,0 +1,64 @@
|
||||||
|
|
||||||
|
[[_saml-jetty9-per-war]]
|
||||||
|
===== Jetty 9 Per WAR Configuration
|
||||||
|
|
||||||
|
This section describes how to secure a WAR directly by adding config and editing files within your WAR package.
|
||||||
|
|
||||||
|
The first thing you must do is create a `WEB-INF/jetty-web.xml` file in your WAR package.
|
||||||
|
This is a Jetty specific config file and you must define a Keycloak specific authenticator within it.
|
||||||
|
|
||||||
|
[source,xml]
|
||||||
|
----
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
|
||||||
|
<Configure class="org.eclipse.jetty.webapp.WebAppContext">
|
||||||
|
<Get name="securityHandler">
|
||||||
|
<Set name="authenticator">
|
||||||
|
<New class="org.keycloak.adapters.saml.jetty.KeycloakSamlAuthenticator">
|
||||||
|
</New>
|
||||||
|
</Set>
|
||||||
|
</Get>
|
||||||
|
</Configure>
|
||||||
|
----
|
||||||
|
|
||||||
|
Next you must create a `keycloak-saml.xml` adapter config file within the `WEB-INF` directory of your WAR.
|
||||||
|
The format of this config file is describe in the <<fake/../../../../saml/java/general-config.adoc#_saml-general-config,General Adapter Config>> section.
|
||||||
|
|
||||||
|
Finally you must specify both a `login-config` and use standard servlet security to specify role-base constraints on your URLs.
|
||||||
|
Here's an example:
|
||||||
|
|
||||||
|
[source,xml]
|
||||||
|
----
|
||||||
|
<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">
|
||||||
|
|
||||||
|
<module-name>customer-portal</module-name>
|
||||||
|
|
||||||
|
<security-constraint>
|
||||||
|
<web-resource-collection>
|
||||||
|
<web-resource-name>Customers</web-resource-name>
|
||||||
|
<url-pattern>/*</url-pattern>
|
||||||
|
</web-resource-collection>
|
||||||
|
<auth-constraint>
|
||||||
|
<role-name>user</role-name>
|
||||||
|
</auth-constraint>
|
||||||
|
<user-data-constraint>
|
||||||
|
<transport-guarantee>CONFIDENTIAL</transport-guarantee>
|
||||||
|
</user-data-constraint>
|
||||||
|
</security-constraint>
|
||||||
|
|
||||||
|
<login-config>
|
||||||
|
<auth-method>BASIC</auth-method>
|
||||||
|
<realm-name>this is ignored currently</realm-name>
|
||||||
|
</login-config>
|
||||||
|
|
||||||
|
<security-role>
|
||||||
|
<role-name>admin</role-name>
|
||||||
|
</security-role>
|
||||||
|
<security-role>
|
||||||
|
<role-name>user</role-name>
|
||||||
|
</security-role>
|
||||||
|
</web-app>
|
||||||
|
----
|
6
securing_apps/topics/saml/java/logout.adoc
Normal file
6
securing_apps/topics/saml/java/logout.adoc
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
==== Logout
|
||||||
|
|
||||||
|
There are multiple ways you can logout from a web application.
|
||||||
|
For Java EE servlet containers, you can call `HttpServletRequest.logout()`. For any other browser application, you can point
|
||||||
|
the browser at any url of your web application that has a security constraint and pass in a query parameter GLO, i.e. `$$http://myapp?GLO=true$$`.
|
||||||
|
This will log you out if you have an SSO session with your browser.
|
18
securing_apps/topics/saml/java/saml-jboss-adapter.adoc
Normal file
18
securing_apps/topics/saml/java/saml-jboss-adapter.adoc
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
[[_saml_jboss_adapter]]
|
||||||
|
|
||||||
|
{% if book.community %}
|
||||||
|
==== JBoss EAP/Wildfly Adapter
|
||||||
|
{% endif %}
|
||||||
|
{% if book.product %}
|
||||||
|
==== JBoss EAP Adapter
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if book.community %}
|
||||||
|
To be able to secure WAR apps deployed on JBoss EAP or Wildfly, you must install and configure the {{book.project.name}} SAML Adapter Subsystem.
|
||||||
|
{% endif %}
|
||||||
|
{% if book.product %}
|
||||||
|
To be able to secure WAR apps deployed on JBoss EAP, you must install and configure the {{book.project.name}} SAML Adapter Subsystem.
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
You then provide a keycloak config, `/WEB-INF/keycloak-saml.xml` file in your WAR and change the auth-method to KEYCLOAK-SAML within web.xml.
|
||||||
|
Both methods are described in this section.
|
|
@ -0,0 +1,6 @@
|
||||||
|
= Overview
|
||||||
|
|
||||||
|
This document describes the Keycloak SAML client adapter and how it can be configured for a variety of platforms.
|
||||||
|
The Keycloak SAML client adapter is a standalone component that provides generic SAML 2.0 support for your web applications.
|
||||||
|
There are no Keycloak server extensions built into it.
|
||||||
|
As long as the Identity Provider (IdP) being communicated with supports standard SAML, the Keycloak SAML client adapter should be able to integrate with it.
|
57
securing_apps/topics/saml/java/servlet-filter-adapter.adoc
Normal file
57
securing_apps/topics/saml/java/servlet-filter-adapter.adoc
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
|
||||||
|
==== Java Servlet Filter Adapter
|
||||||
|
|
||||||
|
If you want to use SAML with a Java servlet application that doesn't have an adapter for that servlet platform, you can
|
||||||
|
opt to use the servlet filter adapter that {{book.project.name}} has.
|
||||||
|
This adapter works a little differently than the other adapters.
|
||||||
|
You still have to specify a `/WEB-INF/keycloak-saml.xml` file as defined in
|
||||||
|
the <<fake/../../../../saml/java/general-config.adoc#_saml-general-config,General Adapter Config>> section, but
|
||||||
|
you do not define security constraints in _web.xml_.
|
||||||
|
Instead you define a filter mapping using the {{book.project.name}} servlet filter adapter to secure the url patterns you want to secure.
|
||||||
|
|
||||||
|
NOTE: Backchannel logout works a bit differently than the standard adapters.
|
||||||
|
Instead of invalidating the http session it instead marks the session ID as logged out.
|
||||||
|
There's just no way of arbitrarily invalidating an http session based on a session ID.
|
||||||
|
|
||||||
|
WARNING: Backchannel logout does not currently work when you have a clustered application that uses the SAML filter.
|
||||||
|
|
||||||
|
[source,xml]
|
||||||
|
----
|
||||||
|
<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">
|
||||||
|
|
||||||
|
<module-name>customer-portal</module-name>
|
||||||
|
|
||||||
|
<filter>
|
||||||
|
<filter-name>Keycloak Filter</filter-name>
|
||||||
|
<filter-class>org.keycloak.adapters.saml.servlet.SamlFilter</filter-class>
|
||||||
|
</filter>
|
||||||
|
<filter-mapping>
|
||||||
|
<filter-name>Keycloak Filter</filter-name>
|
||||||
|
<url-pattern>/*</url-pattern>
|
||||||
|
</filter-mapping>
|
||||||
|
</web-app>
|
||||||
|
----
|
||||||
|
|
||||||
|
The {{book.project.name}} filter has the same configuration parameters available as the other adapters except you must
|
||||||
|
define them as filter init params instead of context params.
|
||||||
|
|
||||||
|
You can define multiple filter mappings if you have various different secure and unsecure url patterns.
|
||||||
|
|
||||||
|
WARNING: You must have a filter mapping that covers `/saml`.
|
||||||
|
This mapping covers all server callbacks.
|
||||||
|
|
||||||
|
When registering SPs with an IdP, you must register `http[s]://hostname/{context-root}/saml` as your Assert Consumer Service URL and Single Logout Service URL.
|
||||||
|
|
||||||
|
To use this filter, include this maven artifact in your WAR poms:
|
||||||
|
|
||||||
|
[source,xml,subs="attributes+"]
|
||||||
|
----
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.keycloak</groupId>
|
||||||
|
<artifactId>keycloak-saml-servlet-filter-adapter</artifactId>
|
||||||
|
<version>{{book.project.versionMvn}}</version>
|
||||||
|
</dependency>
|
||||||
|
----
|
8
securing_apps/topics/saml/java/tomcat-adapter.adoc
Normal file
8
securing_apps/topics/saml/java/tomcat-adapter.adoc
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
[[_saml-tomcat-adapter]]
|
||||||
|
|
||||||
|
==== Tomcat SAML adapters
|
||||||
|
|
||||||
|
To be able to secure WAR apps deployed on Tomcat 6, 7 and 8 you must install the Keycloak Tomcat 6, 7 or 8 SAML adapter into your Tomcat installation.
|
||||||
|
You then have to provide some extra configuration in each WAR you deploy to Tomcat.
|
||||||
|
Let's go over these steps.
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
|
||||||
|
[[_saml-tomcat-adapter-installation]]
|
||||||
|
===== Adapter Installation
|
||||||
|
|
||||||
|
Adapters are no longer included with the appliance or war distribution.
|
||||||
|
Each adapter is a separate download on the Keycloak download site.
|
||||||
|
They are also available as a maven artifact.
|
||||||
|
|
||||||
|
You must unzip the adapter distro into Tomcat's `lib/` directory.
|
||||||
|
Including adapter's jars within your WEB-INF/lib directory will not work! The Keycloak SAML adapter is implemented as
|
||||||
|
a Valve and valve code must reside in Tomcat's main lib/ directory.
|
||||||
|
|
||||||
|
|
||||||
|
[source]
|
||||||
|
----
|
||||||
|
$ cd $TOMCAT_HOME/lib
|
||||||
|
$ unzip keycloak-saml-tomcat6-adapter-dist.zip
|
||||||
|
or
|
||||||
|
$ unzip keycloak-saml-tomcat7-adapter-dist.zip
|
||||||
|
or
|
||||||
|
$ unzip keycloak-saml-tomcat8-adapter-dist.zip
|
||||||
|
----
|
||||||
|
|
|
@ -0,0 +1,53 @@
|
||||||
|
|
||||||
|
===== Per WAR Configuration
|
||||||
|
|
||||||
|
This section describes how to secure a WAR directly by adding config and editing files within your WAR package.
|
||||||
|
|
||||||
|
The first thing you must do is create a `META-INF/context.xml` file in your WAR package.
|
||||||
|
This is a Tomcat specific config file and you must define a Keycloak specific Valve.
|
||||||
|
|
||||||
|
[source,xml]
|
||||||
|
----
|
||||||
|
<Context path="/your-context-path">
|
||||||
|
<Valve className="org.keycloak.adapters.saml.tomcat.SamlAuthenticatorValve"/>
|
||||||
|
</Context>
|
||||||
|
----
|
||||||
|
|
||||||
|
Next you must create a `keycloak-saml.xml` adapter config file within the `WEB-INF` directory of your WAR.
|
||||||
|
The format of this config file is describe in the <<fake/../../../../saml/java/general-config.adoc#_saml-general-config,General Adapter Config>> section.
|
||||||
|
|
||||||
|
Finally you must specify both a `login-config` and use standard servlet security to specify role-base constraints on your URLs.
|
||||||
|
Here's an example:
|
||||||
|
|
||||||
|
[source,xml]
|
||||||
|
----
|
||||||
|
<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">
|
||||||
|
|
||||||
|
<module-name>customer-portal</module-name>
|
||||||
|
|
||||||
|
<security-constraint>
|
||||||
|
<web-resource-collection>
|
||||||
|
<web-resource-name>Customers</web-resource-name>
|
||||||
|
<url-pattern>/*</url-pattern>
|
||||||
|
</web-resource-collection>
|
||||||
|
<auth-constraint>
|
||||||
|
<role-name>user</role-name>
|
||||||
|
</auth-constraint>
|
||||||
|
</security-constraint>
|
||||||
|
|
||||||
|
<login-config>
|
||||||
|
<auth-method>BASIC</auth-method>
|
||||||
|
<realm-name>this is ignored currently</realm-name>
|
||||||
|
</login-config>
|
||||||
|
|
||||||
|
<security-role>
|
||||||
|
<role-name>admin</role-name>
|
||||||
|
</security-role>
|
||||||
|
<security-role>
|
||||||
|
<role-name>user</role-name>
|
||||||
|
</security-role>
|
||||||
|
</web-app>
|
||||||
|
----
|
205
securing_apps/topics/saml/mod-auth-mellon.adoc
Normal file
205
securing_apps/topics/saml/mod-auth-mellon.adoc
Normal file
|
@ -0,0 +1,205 @@
|
||||||
|
[[_mod_auth_mellon]]
|
||||||
|
|
||||||
|
=== mod_auth_mellon Apache HTTPD Module
|
||||||
|
|
||||||
|
The https://github.com/UNINETT/mod_auth_mellon[mod_auth_mellon] module is an Apache HTTPD plugin for SAML. If your language/environment supports using Apache HTTPD as a proxy, then you can use mod_auth_mellon to secure your web application with SAML. <do we still want to refer users to the github repo?>For more details on this module see the _mod_auth_mellon_ Github repo.
|
||||||
|
|
||||||
|
To configure mod_auth_mellon you'll need:
|
||||||
|
|
||||||
|
* An Identity Provider (IdP) entity descriptor XML file, which describes the connection to {{book.project.name}} or another SAML IdP
|
||||||
|
* An SP entity descriptor XML file, which describes the SAML connections and configuration for the application you are securing.
|
||||||
|
* A private key PEM file, which is a text file in the PEM format that defines the private key the application uses to sign documents.
|
||||||
|
* A certificate PEM file, which is a text file that defines the certificate for your application.
|
||||||
|
* mod_auth_mellon-specific Apache HTTPD module configuration.
|
||||||
|
|
||||||
|
If you have already defined and registered the client application within a realm on the {{book.project.name}} application server,
|
||||||
|
{{book.project.name}} can generate all the files you need except the Apache HTTPD module configuration.
|
||||||
|
To generate the Apache HTTPD module configuration, complete the following steps:
|
||||||
|
|
||||||
|
. Go to the *Installation* page of your SAML client and select the *Mod Auth Mellon files* option.
|
||||||
|
+
|
||||||
|
.mod_auth_mellon config download
|
||||||
|
image:../../{{book.images}}/mod-auth-mellon-config-download.png[]
|
||||||
|
|
||||||
|
. Click *Download* to download a zip file that contains the XML descriptor and PEM files you need.
|
||||||
|
|
||||||
|
<How does the following content relate to the previous paragraph?>
|
||||||
|
|
||||||
|
==== Configuring mod_auth_mellon with Red Hat Single Sign-On
|
||||||
|
|
||||||
|
There are two hosts involved:
|
||||||
|
|
||||||
|
*The host on which Red Hat Single Sign-On is running, which will be referred to as $idp_host because Red Hat Single Sign-On is a SAML identity provider (IdP).
|
||||||
|
|
||||||
|
*The host on which the web application is running, which will be referred to as $sp_host. In SAML an application using an IdP is called a service provider (SP).
|
||||||
|
|
||||||
|
All of the following steps need to performed on $sp_host with root privileges.
|
||||||
|
|
||||||
|
===== Installing the Packages
|
||||||
|
|
||||||
|
To install the necessary packages, you will need:
|
||||||
|
|
||||||
|
* Apache Web Server (httpd)
|
||||||
|
* Mellon SAML SP add-on module for Apache
|
||||||
|
* Tools to create X509 certificates
|
||||||
|
|
||||||
|
To install the necessary packages, run this command:
|
||||||
|
|
||||||
|
`yum install httpd mod_auth_mellon mod_ssl openssl`
|
||||||
|
|
||||||
|
===== Creating a Configuration Directory for Apache SAML
|
||||||
|
|
||||||
|
It is advisable to keep configuration files related to Apache's use of SAML in one location.
|
||||||
|
|
||||||
|
Create a new directory named saml2 located under the Apache configuration root /etc/httpd:
|
||||||
|
|
||||||
|
`mkdir /etc/httpd/saml2`
|
||||||
|
|
||||||
|
===== Configuring the Mellon Service Provider
|
||||||
|
|
||||||
|
Configuration files for Apache add-on modules are located in the /etc/httpd/conf.d directory and have a file name extension of .conf. You need to create the /etc/httpd/conf.d/mellon.conf file and place Mellon's configuration directives in it.
|
||||||
|
|
||||||
|
Mellon's configuration directives can roughly be broken down into two classes of information:
|
||||||
|
|
||||||
|
* Which URLs to protect with SAML authentication
|
||||||
|
* What SAML parameters will be used when a protected URL is referenced.
|
||||||
|
|
||||||
|
Apache configuration directives typically follow a hierarchical tree structure in the URL space, which are known as locations. You need to specify one or more URL locations for Mellon to protect. You have flexibility in how you add the configuration parameters that apply to each location. You can either add all the necessary parameters to the location block or you can add Mellon parameters to a common location high up in the URL location hierarchy that specific protected locations inherit (or some combination of the two). Since it is common for an SP to operate in the same way no matter which location triggers SAML actions, the example configuration used here places common Mellon configuration directives in the root of the hierarchy and then specific locations to be protected by Mellon can be defined with minimal directives. This strategy avoids duplicating the same parameters for each protected location.
|
||||||
|
|
||||||
|
This example has just one protected location: https://$sp_host/protected.
|
||||||
|
|
||||||
|
To configure the Mellon service provider, complete the following steps:
|
||||||
|
|
||||||
|
. Create the file /etc/httpd/conf.d/mellon.conf with this content:
|
||||||
|
|
||||||
|
<Location / >
|
||||||
|
MellonEnable info
|
||||||
|
MellonEndpointPath /mellon/
|
||||||
|
MellonSPMetadataFile /etc/httpd/saml2/mellon_metadata.xml
|
||||||
|
MellonSPPrivateKeyFile /etc/httpd/saml2/mellon.key
|
||||||
|
MellonSPCertFile /etc/httpd/saml2/mellon.crt
|
||||||
|
MellonIdPMetadataFile /etc/httpd/saml2/idp_metadata.xml
|
||||||
|
</Location>
|
||||||
|
|
||||||
|
<Location /private >
|
||||||
|
AuthType Mellon
|
||||||
|
MellonEnable auth
|
||||||
|
Require valid-user
|
||||||
|
</Location>
|
||||||
|
|
||||||
|
Note: Some of the files referenced in the code above are created in later steps.
|
||||||
|
|
||||||
|
===== Creating the Service Provider Metadata
|
||||||
|
|
||||||
|
In SAML IdPs and SPs exchange SAML metadata, which is in XML format. The schema for the metadata is a standard, thus assuring participating SAML entities can consume each other's metadata. You need:
|
||||||
|
|
||||||
|
* Metadata for the IdP that the SP utilizes
|
||||||
|
* Metadata describing the SP provided to the IdP
|
||||||
|
|
||||||
|
One of the components of SAML metadata is X509 certificates. These certificates are used for two purposes:
|
||||||
|
|
||||||
|
* Sign SAML messages so the receiving end can prove the message originated from the expected party.
|
||||||
|
* Encrypt the message during transport (seldom used because SAML messages typically occur on TLS-protected transports)
|
||||||
|
|
||||||
|
You can use your own certificates if you already have a Certificate Authority (CA) or you can generate a self-signed certificate. For simplicity in this example a self-signed certificate is used.
|
||||||
|
|
||||||
|
Because Mellon's SP metadata must reflect the capabilities of the installed version of mod_auth_mellon, must be valid SP metadata XML, and must contain an X509 certificate (whose creation can be obtuse unless you are familiar with X509 certificate generation) the most expedient way to produce the SP metadata is to use a tool included in the mod_auth_mellon package (mellon_create_metadata.sh). The generated metadata can always be edited later because it is a text file. The tool also creates your X509 key and certificate.
|
||||||
|
|
||||||
|
SAML IdPs and SPs identify themselves using a unique name known as an EntityID. To use the Mellon metadata creation tool you need:
|
||||||
|
|
||||||
|
* The EntityID, which is typically the URL of the SP, and often the URL of the SP where the SP metadata can be retrieved
|
||||||
|
* The URL where SAML messages for the SP will be consumed, which Mellon calls the MellonEndPointPath.
|
||||||
|
|
||||||
|
To create the SP metadata, complete the following steps:
|
||||||
|
|
||||||
|
. Create a few helper shell variables:
|
||||||
|
+
|
||||||
|
fqdn=`hostname`
|
||||||
|
mellon_endpoint_url="https://${fqdn}/mellon"
|
||||||
|
mellon_entity_id="${mellon_endpoint_url}/metadata"
|
||||||
|
file_prefix="$(echo "$mellon_entity_id" | sed 's/[^A-Za-z.]/_/g' | sed 's/__*/_/g')"
|
||||||
|
|
||||||
|
. Invoke the Mellon metadata creation tool by running this command:
|
||||||
|
+
|
||||||
|
`/usr/libexec/mod_auth_mellon/mellon_create_metadata.sh $mellon_entity_id $mellon_endpoint_url`
|
||||||
|
|
||||||
|
. Move the generated files to their destination (referenced in the /etc/httpd/conf.d/mellon.conf file created above):
|
||||||
|
+
|
||||||
|
mv ${file_prefix}.cert /etc/httpd/saml2/mellon.crt
|
||||||
|
mv ${file_prefix}.key /etc/httpd/saml2/mellon.key
|
||||||
|
mv ${file_prefix}.xml /etc/httpd/saml2/mellon_metadata.xml
|
||||||
|
|
||||||
|
===== Adding the Mellon Service Provider to the Red Hat Single Sign-On Identity Provider
|
||||||
|
|
||||||
|
Assumption: The Red Hat Single Sign-On IdP has already been installed on the $idp_host.
|
||||||
|
|
||||||
|
Red Hat Single Sign-On supports multiple tenancy where all users, clients, and so on are grouped in what is called a realm. Each realm is independent of other realms. You can use an existing realm in your Red Hat Single Sign-On, but this example shows how to create a new realm called test_realm and use that realm.
|
||||||
|
|
||||||
|
All these operations are performed using the Red Hat Single Sign-On administration web console. You must have the admin username and password for $idp_host.
|
||||||
|
|
||||||
|
To complete the following steps:
|
||||||
|
|
||||||
|
. Open the Admin Console and log on by entering the admin username and password.
|
||||||
|
+
|
||||||
|
After logging into the admin console there will be an existing realm. When Red Hat Single Sign-On is first set up a root realm, master, is created by default. Any previously created realms are listed in the upper left corner of the admin console in a drop-down list.
|
||||||
|
|
||||||
|
. From the realm drop-down list select *Add realm*.
|
||||||
|
|
||||||
|
. In the Name field type `test_realm` and click *Create*.
|
||||||
|
|
||||||
|
====== Adding the Mellon Service Provider as a Client of the Realm
|
||||||
|
|
||||||
|
In Red Hat Single Sign-On SAML SPs are known as clients. To add the SP we must be in the Clients section of the realm.
|
||||||
|
|
||||||
|
. Click the Clients menu item on the left and click *Create* in the upper right corner to create a new client.
|
||||||
|
|
||||||
|
====== Adding the Mellon SP Client
|
||||||
|
|
||||||
|
To add the Mellon SP client, complete the following steps:
|
||||||
|
|
||||||
|
. Set the client protocol to SAML. From the Client Protocol drop down list, select *saml*.
|
||||||
|
. Provide the Mellon SP metadata file created above (/etc/httpd/saml2/mellon_metadata.xml). Depending on where your browser is running you might have to copy the SP metadata from $sp_host to the machine on which your browser is running so the browser can find the file.
|
||||||
|
. Click *Save*.
|
||||||
|
|
||||||
|
====== Editing the Mellon SP Client
|
||||||
|
|
||||||
|
There are several client configuration parameters we suggest setting:
|
||||||
|
|
||||||
|
* Ensure "Force POST Binding" is On.
|
||||||
|
* Add paosResponse to the Valid Redirect URIs list:
|
||||||
|
. Copy the postResponse URL in "Valid Redirect URIs" and paste it into the empty add text fields just below the "+".
|
||||||
|
. Change "postResponse" to "paosResponse". (The paosResponse URL is needed for SAML ECP.)
|
||||||
|
. Click *Save* at the bottom.
|
||||||
|
|
||||||
|
Many SAML SPs determine authorization based on a user's membership in a group. The Red Hat Single Sign-On IdP can manage user group information but it does not supply the user's groups unless the IdP is configured to supply it as a SAML attribute.
|
||||||
|
|
||||||
|
To configure the IdP to supply the user's groups as as a SAML attribute, complete the following steps:
|
||||||
|
|
||||||
|
. Click the Mappers tab of the client.
|
||||||
|
. In the upper right corner of the Mappers page, click *Create*.
|
||||||
|
. From the Mapper Type drop-down list select *Group list*.
|
||||||
|
. Set Name to "group list."
|
||||||
|
. Set the SAML attribute name to "groups."
|
||||||
|
. Click *Save.*
|
||||||
|
|
||||||
|
The remaining steps are performed on $sp_host.
|
||||||
|
|
||||||
|
====== Retrieving the Identity Provider Metadata
|
||||||
|
|
||||||
|
Now that you have created the realm on the IdP you need to retrieve the IdP metadata associated with it so the Mellon SP recognizes it. In the /etc/httpd/conf.d/mellon.conf file created previously, the MellonIdPMetadataFile is specified as /etc/httpd/saml2/idp_metadata.xml but until now that file has not existed on $sp_host. To get that file we will retrieve it from the IdP.
|
||||||
|
|
||||||
|
. Retrieve the file from the IdP by substituting $idp_host with the correct value:
|
||||||
|
curl -k -o /etc/httpd/saml2/idp_metadata.xml \
|
||||||
|
https://$idp_host/auth/realms/test_realm/protocol/saml/descriptor
|
||||||
|
+
|
||||||
|
Mellon is now fully configured.
|
||||||
|
|
||||||
|
. To run a syntax check for Apache configuration files:
|
||||||
|
apachectl configtest
|
||||||
|
+
|
||||||
|
Note: configtest is equivalent to the -t argument to apachectl. If the configuration test shows any errors, correct them before proceeding.
|
||||||
|
|
||||||
|
. Restart the Apache server:
|
||||||
|
systemctl restart httpd.service
|
||||||
|
|
||||||
|
You have now set up both Red Hat Single Sign-On as a SAML IdP in the test_realm and mod_auth_mellon as SAML SP protecting the URL $sp_host/protected (and everything beneath it) by authenticating against the $``$idp_host`` IdP.
|
4
securing_apps/topics/saml/saml-overview.adoc
Normal file
4
securing_apps/topics/saml/saml-overview.adoc
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
== SAML
|
||||||
|
|
||||||
|
This section describes how you can secure applications and services with SAML using either {{book.project.name}} client adapters or generic
|
||||||
|
SAML provider libraries.
|
Loading…
Reference in a new issue