[java-idp-plugin-oidc-rp] branch main updated: First commit

Phil Smart philip.smart at jisc.ac.uk
Thu Jun 3 16:27:31 UTC 2021


This is an automated email from the git hooks/post-receive script.

philsmart pushed a commit to branch main
in repository java-idp-plugin-oidc-rp.

View the commit online:
http://git.shibboleth.net/view/?p=java-idp-plugin-oidc-rp.git;a=commit;h=6c243b73114960e1fd94fa39c9de45b82d8f3a5e

The following commit(s) were added to refs/heads/main by this push:
       new  6c243b7   First commit
6c243b7 is described below

commit 6c243b73114960e1fd94fa39c9de45b82d8f3a5e
Author: Phil Smart <philip.smart at jisc.ac.uk>
AuthorDate: Thu Jun 3 17:27:28 2021 +0100

    First commit
    
    Is an authn pluginification of the existing mpassid oidc rp branch of
    the IdP (feature/mpassid-oidc-int).
---
 .gitignore                                         |  37 +-
 checkstyle.xml                                     | 117 +++++
 java-idp-oidc-rp-api/pom.xml                       |  51 ++
 .../idp/plugin/authn/oidc/rp/Version.java          |  32 ++
 .../oidc/rp/context/OpenIDConnectContext.java      | 454 ++++++++++++++++
 java-idp-oidc-rp-dist/pom.xml                      |  51 ++
 .../src/main/assembly/assembly-tgz.xml             |  72 +++
 .../src/main/assembly/assembly-zip.xml             |  72 +++
 .../src/main/resources/bootstrap/keys.txt          |   1 +
 java-idp-oidc-rp-impl/.checkstyle                  |   7 +
 java-idp-oidc-rp-impl/pom.xml                      | 118 +++++
 .../idp/plugin/authn/oidc/rp/OIDCRPModule.java     |  25 +
 .../idp/plugin/authn/oidc/rp/OIDCRPPlugin.java     |  27 +
 .../oidc/rp/impl/AuthorizationController.java      | 158 ++++++
 .../authn/oidc/rp/impl/GetOIDCTokenResponse.java   | 137 +++++
 .../authn/oidc/rp/impl/SetOIDCInformation.java     | 583 +++++++++++++++++++++
 .../authn/oidc/rp/impl/ValidateIDTokenACR.java     | 103 ++++
 .../oidc/rp/impl/ValidateIDTokenAudience.java      |  91 ++++
 .../rp/impl/ValidateIDTokenAuthenticationTime.java | 185 +++++++
 .../rp/impl/ValidateIDTokenAuthorizedParty.java    |  89 ++++
 .../rp/impl/ValidateIDTokenExpirationTime.java     |  87 +++
 .../authn/oidc/rp/impl/ValidateIDTokenIssuer.java  |  83 +++
 .../oidc/rp/impl/ValidateIDTokenSignature.java     | 159 ++++++
 .../oidc/rp/impl/ValidateOIDCAuthentication.java   | 209 ++++++++
 .../impl/ValidateOIDCAuthenticationResponse.java   | 123 +++++
 .../plugin/authn/oidc/rp/impl/package-info.java    |  22 +
 .../META-INF/net.shibboleth.idp/postconfig.xml     |  48 ++
 .../OIDCRelyingParty/oidc-relying-party-beans.xml  |  79 +++
 .../OIDCRelyingParty/oidc-relying-party-flow.xml   |  60 +++
 .../services/net.shibboleth.idp.module.IdPModule   |   1 +
 .../services/net.shibboleth.idp.plugin.IdPPlugin   |   2 +
 .../authn/oidc/rp/conf/authn/oidc-rp.properties    |   7 +
 .../idp/plugin/authn/oidc/rp/module.properties     |  14 +
 .../idp/plugin/authn/oidc/rp/plugin.properties     |   9 +
 pom.xml                                            | 145 +++++
 35 files changed, 3430 insertions(+), 28 deletions(-)

diff --git a/.gitignore b/.gitignore
index 8eeb321..0ca7416 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,29 +1,10 @@
-# A simulation of Subversion default ignores, generated by reposurgeon.
-*.o
-*.lo
-*.la
-*.al
-*.libs
-*.so
-*.so.[0-9]*
-*.a
-*.pyc
-*.pyo
-*.rej
-*.iml
-*~
-*.#*
-.*.swp
-.DS_store
 .DS_Store
-# Simulated Subversion default ignores end here
-# The contents of the svn:ignore property on the branch root.
-/test-output
-/target
-.factorypath
-classpath:
-/.checkstyle
-/.DS_Store
-target/
-bin/
-test-output/
+*/target
+*/test-output
+*~
+*/.classpath
+*/.project
+*/.settings
+/.project
+/.settings
+target
diff --git a/checkstyle.xml b/checkstyle.xml
new file mode 100644
index 0000000..f3f13f6
--- /dev/null
+++ b/checkstyle.xml
@@ -0,0 +1,117 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE module PUBLIC "-//Puppy Crawl//DTD Check Configuration 1.3//EN" "http://www.puppycrawl.com/dtds/configuration_1_3.dtd">
+
+<!--
+    This configuration file was written by the eclipse-cs plugin configuration editor
+-->
+<!--
+    Checkstyle-Configuration: Shibboleth Checkstyle
+    Description: none
+-->
+<module name="Checker">
+  <property name="severity" value="warning"/>
+  <module name="TreeWalker">
+    <property name="tabWidth" value="4"/>
+    <module name="JavadocMethod"/>
+    <module name="JavadocType">
+        <property name="allowUnknownTags" value="true"/>
+    </module>
+    <module name="JavadocVariable"/>
+    <module name="JavadocStyle">
+      <property name="checkEmptyJavadoc" value="true"/>
+    </module>
+    <module name="ConstantName"/>
+    <module name="LocalFinalVariableName"/>
+    <module name="LocalVariableName"/>
+    <module name="MemberName"/>
+    <module name="MethodName"/>
+    <module name="PackageName"/>
+    <module name="ParameterName"/>
+    <module name="StaticVariableName"/>
+    <module name="TypeName"/>
+    <module name="AvoidStarImport"/>
+    <module name="IllegalImport"/>
+    <module name="RedundantImport"/>
+    <module name="UnusedImports"/>
+    <module name="MethodLength">
+      <property name="max" value="70"/>
+    </module>
+    <module name="ParameterNumber">
+      <property name="max" value="5"/>
+    </module>
+    <module name="EmptyForIteratorPad"/>
+    <module name="MethodParamPad"/>
+    <module name="ModifierOrder"/>
+    <module name="AvoidNestedBlocks"/>
+    <module name="LeftCurly"/>
+    <module name="NeedBraces"/>
+    <module name="RightCurly"/>
+    <module name="EmptyStatement"/>
+    <module name="EqualsHashCode"/>
+    <module name="HiddenField"/>
+    <module name="IllegalInstantiation"/>
+    <module name="InnerAssignment"/>
+    <module name="MissingSwitchDefault"/>
+    <module name="SimplifyBooleanExpression"/>
+    <module name="SimplifyBooleanReturn"/>
+    <module name="FinalClass"/>
+    <module name="HideUtilityClassConstructor"/>
+    <module name="VisibilityModifier"/>
+    <module name="ArrayTypeStyle"/>
+    <module name="UpperEll"/>
+    <module name="AnonInnerLength"/>
+    <module name="EmptyForInitializerPad"/>
+    <module name="CovariantEquals"/>
+    <module name="DefaultComesLast"/>
+    <module name="DeclarationOrder"/>
+    <module name="ExplicitInitialization"/>
+    <module name="FallThrough"/>
+    <module name="IllegalThrows"/>
+    <module name="MultipleVariableDeclarations"/>
+    <module name="PackageDeclaration"/>
+    <module name="ParameterAssignment"/>
+    <module name="ReturnCount">
+      <property name="max" value="8"/>
+      <property name="maxForVoid" value="8"/>
+    </module>
+    <module name="StringLiteralEquality"/>
+    <module name="SuperFinalize"/>
+    <module name="ArrayTrailingComma"/>
+    <module name="UnnecessaryParentheses"/>
+    <module name="MutableException"/>
+    <module name="ThrowsCount">
+      <property name="max" value="3"/>
+    </module>
+    <module name="CyclomaticComplexity"/>
+    <module name="TrailingComment"/>
+    <module name="EqualsAvoidNull"/>
+    <module name="ModifiedControlVariable"/>
+    <module name="FinalParameters">
+      <property name="tokens" value="METHOD_DEF,CTOR_DEF,LITERAL_CATCH"/>
+    </module>
+    <module name="FinalLocalVariable">
+      <property name="tokens" value="PARAMETER_DEF,VARIABLE_DEF"/>
+      <property name="validateEnhancedForLoopVariable" value="true"/>
+    </module>
+    <module name="SuppressionCommentFilter">
+      <property name="offCommentFormat" value="\bCheck[Ss]tyle:\s*([\w|]+)\s+OFF\b"/>
+      <property name="onCommentFormat" value="\bCheck[Ss]tyle:\s*([\w|]+)\s+ON\b"/>
+      <property name="checkFormat" value="$1"/>
+    </module>
+    <module name="MissingJavadocMethod"/>
+    <module name="MissingJavadocPackage"/>
+    <module name="MissingJavadocType"/>
+    <module name="InvalidJavadocPosition"/>
+  </module>
+  <module name="FileTabCharacter"/>
+  <module name="FileLength">
+    <property name="max" value="1000"/>
+  </module>
+  <module name="Header">
+    <property name="header" value="/*\n * Licensed to the University Corporation for Advanced Internet Development,\n * Inc. (UCAID) under one or more contributor license agreements.  See the\n * NOTICE file distributed with this work for additional information regarding\n * copyright ownership. The UCAID licenses this file to You under the Apache\n * License, Version 2.0 (the "License"); you may not use this file except in\n * compliance with the License.  You may obtain a cop [...]
+  </module>
+  <module name="JavadocPackage"/>
+  <module name="LineLength">
+    <property name="max" value="120"/>
+  </module>
+</module>
diff --git a/java-idp-oidc-rp-api/pom.xml b/java-idp-oidc-rp-api/pom.xml
new file mode 100644
index 0000000..67d9a6d
--- /dev/null
+++ b/java-idp-oidc-rp-api/pom.xml
@@ -0,0 +1,51 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+
+	<parent>
+		<groupId>net.shibboleth.idp.plugin.authn</groupId>
+		<artifactId>idp-plugin-oidc-rp-parent</artifactId>
+		<version>0.0.1-SNAPSHOT</version>
+	</parent>
+
+	<artifactId>idp-plugin-oidc-rp-api</artifactId>
+	<packaging>jar</packaging>
+	<name>Shibboleth IdP :: Plugins :: OIDC RP Login Flow API</name>
+	<description>IdP java-idp-oidc-rp plugin API.</description>
+
+	<properties>
+		<checkstyle.configLocation>${project.basedir}/../checkstyle.xml</checkstyle.configLocation>
+		<automatic.module.name>net.shibboleth.idp.plugin.authn.oidc.rp.api</automatic.module.name>
+	</properties>
+
+	<dependencies>
+		<!-- Provided dependencies -->
+		<dependency>
+			<groupId>com.google.code.findbugs</groupId>
+			<artifactId>jsr305</artifactId>
+			<scope>provided</scope>
+		</dependency>
+		 <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>javax.servlet-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+         <dependency>
+            <groupId>com.nimbusds</groupId>
+            <artifactId>nimbus-jose-jwt</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>${idp.groupId}</groupId>
+            <artifactId>idp-authn-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.nimbusds</groupId>
+            <artifactId>oauth2-oidc-sdk</artifactId>
+            <scope>provided</scope>
+        </dependency>    
+	</dependencies>
+	
+</project>
\ No newline at end of file
diff --git a/java-idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/Version.java b/java-idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/Version.java
new file mode 100644
index 0000000..da5b500
--- /dev/null
+++ b/java-idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/Version.java
@@ -0,0 +1,32 @@
+package net.shibboleth.idp.plugin.authn.oidc.rp;
+
+import javax.annotation.Nullable;
+
+/** Class for getting and printing the version of the plugin. */
+public final class Version {
+
+    /** IdP version. */
+    @Nullable private static final String VERSION = Version.class.getPackage().getImplementationVersion();
+
+    /** Constructor. */
+    private Version() {
+    }
+
+    /**
+     * Main entry point to program.
+     * 
+     * @param args command line arguments
+     */
+    public static void main(final String[] args) {
+        System.out.println(VERSION);
+    }
+
+    /**
+     * Get the version of the plugin.
+     * 
+     * @return version of the plugin
+     */
+    @Nullable public static String getVersion() {
+        return VERSION;
+    }
+}
\ No newline at end of file
diff --git a/java-idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/context/OpenIDConnectContext.java b/java-idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/context/OpenIDConnectContext.java
new file mode 100644
index 0000000..7c16c92
--- /dev/null
+++ b/java-idp-oidc-rp-api/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/context/OpenIDConnectContext.java
@@ -0,0 +1,454 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.idp.plugin.authn.oidc.rp.context;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.List;
+import java.util.Map;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import javax.servlet.http.HttpServletRequest;
+
+import net.shibboleth.idp.attribute.IdPAttribute;
+import net.shibboleth.idp.authn.context.AuthenticationContext;
+import net.shibboleth.utilities.java.support.annotation.constraint.Live;
+import net.shibboleth.utilities.java.support.annotation.constraint.NonnullElements;
+import net.shibboleth.utilities.java.support.annotation.constraint.NotLive;
+import net.shibboleth.utilities.java.support.annotation.constraint.Unmodifiable;
+import net.shibboleth.utilities.java.support.logic.Constraint;
+
+import org.opensaml.messaging.context.BaseContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Predicates;
+import com.google.common.collect.Collections2;
+import com.google.common.collect.ImmutableList;
+import com.nimbusds.jwt.JWT;
+import com.nimbusds.oauth2.sdk.Scope;
+import com.nimbusds.oauth2.sdk.auth.Secret;
+import com.nimbusds.oauth2.sdk.id.ClientID;
+import com.nimbusds.oauth2.sdk.id.State;
+import com.nimbusds.openid.connect.sdk.AuthenticationSuccessResponse;
+import com.nimbusds.openid.connect.sdk.Display;
+import com.nimbusds.openid.connect.sdk.Nonce;
+import com.nimbusds.openid.connect.sdk.OIDCTokenResponse;
+import com.nimbusds.openid.connect.sdk.Prompt;
+import com.nimbusds.openid.connect.sdk.claims.ACR;
+import com.nimbusds.openid.connect.sdk.op.OIDCProviderMetadata;
+
+
+/**
+ * Context that carries OpenID Connect client and provider metadata used during authentication of a subject from
+ * an OpenID Connect Provider.
+ * 
+ * <p>Any OPTIONAL parameters of an ODIC authentication request are allowably <code>null</code> - even Lists.
+ * For a list of OPTIONAL authn parameters, see OpenID Connect Core 1.0 section 3.1.2.1</p>
+ *
+ * 
+ * @parent {@link AuthenticationContext}
+ * @added After the RelyingPartyUIContext has been added, before external redirect to the OpenID Connect servlet. 
+ * 
+ * @since 4.0.0
+ */
+public class OpenIDConnectContext extends BaseContext {
+
+    /** Class logger. */
+    @Nonnull private final Logger log = LoggerFactory.getLogger(OpenIDConnectContext.class);
+
+    /** Client Id. */
+    @Nullable private ClientID clientID;
+
+    /** Client Secret. */
+    @Nullable private Secret clientSecret;
+
+    /** Scope. Must contain an openid scope.*/
+    @Nullable private Scope scope;
+
+    /**
+     *  OIDC Prompt. Specifies whether the Authorization Server prompts
+     *  the End-User for re-authentication and consent.
+     */
+    @Nullable private Prompt prompt;
+
+    /** OIDC Authentication Class Reference values.*/
+    @Nullable @NonnullElements private List<ACR> acrs;
+
+    /**
+     *  OIDC Display. Value that specifies how the Authorization Server
+     *  displays the authentication and consent user interface pages to the End-User. 
+     */
+    @Nullable private Display display;
+
+    /** OIDC provider metadata. */
+    @Nullable private OIDCProviderMetadata oIDCProviderMetadata;
+
+    /** OIDC authentication request URI. */
+    @Nullable private URI authenticationRequestURI;
+
+    /** OIDC authentication response URI. */
+    @Nullable private URI authenticationResponseURI;
+
+    /** OIDC authentication success response. */
+    @Nullable private AuthenticationSuccessResponse authSuccessResponse;
+
+    /** OIDC token response. */
+    @Nullable private OIDCTokenResponse oidcTknResponse;
+
+    /** 
+     * State parameter. Opaque value used to maintain state between the request 
+     * and the callback. Typically for CSRF protection. 
+     */
+    @Nullable private State state;
+
+    /** 
+     * Nonce parameter. String value used to associate a Client session with an 
+     * ID Token, and to mitigate replay attacks.
+     */
+    @Nullable private Nonce nonce;
+
+    /** Redirect URI to which the response will be sent. */
+    @Nullable private URI redirectURI;
+
+    /** ID Token from the token endpoint response. */
+    @Nullable private JWT idToken;
+
+    /** Resolved attributes. */
+    //TODO Resolved Attributes never get added to the Context.
+    @Nullable private Map<String, IdPAttribute> resolvedIdPAttributes;
+    
+
+    /**
+     * Get the resolved attributes.
+     * 
+     * @return resolved attributes, may be null.
+     */
+    @Nullable @Live public Map<String, IdPAttribute> getResolvedIdPAttributes() {
+        return resolvedIdPAttributes;
+    }
+
+    /**
+     * Set resolved attributes to context to help form requested objects.
+     * 
+     * @param idPAttributes resolved attributes
+     */
+    public void setResolvedIdPAttributes(@Nullable final Map<String, IdPAttribute> idPAttributes) {
+        resolvedIdPAttributes = idPAttributes;
+    }
+
+    /**
+     * Get the id token received from OpenID Connect Provider.
+     * 
+     * @return id token or <code>null</code> if not set.
+     */
+    @Nullable public JWT getIDToken() {
+        return idToken;
+    }
+
+    /**
+     * Set the id token received from OpenID Connect Provider.
+     * 
+     * @param token from the OpenID Connect Provider.
+     */
+    public void setIDToken(@Nullable final JWT token) {
+        idToken = token;
+    }
+
+    /**
+     * Get the redirect URI of the client.
+     * 
+     * @return redirect uri
+     */
+    @Nullable public URI getRedirectURI() {
+        return redirectURI;
+    }
+
+    /**
+     * Set the redirect URI of the client. The redirect URI is required for authorisation code flows. 
+     * 
+     * @param uri of the client, must not be <code>null</code>.
+     */
+    public void setRedirectURI(@Nonnull final URI uri) {
+        redirectURI = Constraint.isNotNull(uri, "Redirect URI cannot be null");
+    }
+
+    /**
+     * Get the OAuth2 client id.
+     * 
+     * @return client id.
+     */
+    @Nullable public ClientID getClientID() {
+        return clientID;
+    }
+
+    /**
+     * Set the Oauth2 client id. The client id can not be <code>null</code>.
+     * 
+     * @param id Oauth2 Client ID, must not be <code>null</code>.
+     */
+    public void setClientID(@Nonnull final ClientID id) {
+        clientID = Constraint.isNotNull(id, "Client ID cannot be null");
+    }
+
+    /**
+     * Set the client secret.
+     * 
+     * @param secret of the client, must not be <code>null</code>.
+     */
+    public void setClientSecret(@Nonnull final Secret secret) {
+        clientSecret = Constraint.isNotNull(secret, "Client secret cannot be null");
+    }
+    
+    /**
+     * Get the client secret.
+     * 
+     * @return client secret.
+     */
+    @Nullable public Secret getClientSecret() {
+        return clientSecret;
+    }
+
+    /**
+     * Get the scope used for authentication request.
+     * 
+     * @return scope
+     */
+    public Scope getScope() {
+        return scope;
+    }
+
+    /**
+     * Set the scope used for forming an authentication request.
+     * 
+     * @param scp scope of the request, must not be <code>null</code>
+     */
+    public void setScope(@Nonnull final Scope scp) {
+        scope = Constraint.isNotNull(scp, "Scope cannot be null");
+    }
+
+    /**
+     * Get the value for prompt used for forming an authentication request.
+     * 
+     * @return prompt
+     */
+    @Nullable public Prompt getPrompt() {
+        return prompt;
+    }
+
+    /**
+     * Set the value for prompt used for forming an authentication request.
+     * 
+     * @param prmpt used for forming an authentication request
+     */
+    public void setPrompt(@Nullable final Prompt prmpt) {
+        prompt = prmpt;
+    }
+
+    /**
+     * Get the values for acr used for forming authentication request. 
+     * Allowably a <code>null</code> {@link List} if not set.
+     * 
+     * @return acrs or <code>null</code> if not set.
+     */
+    @Nullable @NonnullElements @Unmodifiable @NotLive public List<ACR> getAcrs() {
+       return acrs;
+    }
+
+    /**
+     * Set the values for acr used for forming authentication request. Sets a 
+     * <code>null</code> {@link List} if no ACRs are defined.
+     * 
+     * @param acrList values used for forming authentication request
+     */
+    public void setAcrs(@Nullable @NonnullElements final List<ACR> acrList) {
+        if (acrList!=null) {
+            ImmutableList.copyOf(Collections2.filter(acrList, Predicates.notNull()));
+        } else {
+            acrs = null;           
+        }
+
+    }
+
+    /**
+     * Get the display value used for forming authentication request.
+     * 
+     * @return display
+     */
+    @Nullable public Display getDisplay() {
+        return display;
+    }
+
+    /**
+     * Set the display value used for forming authentication request.
+     * 
+     * @param dspl value for forming authentication request.
+     */
+    public void setDisplay(@Nullable final Display dspl) {
+        display = dspl;
+    }
+
+    /**
+     * Get OpenID Connect Provider metadata.
+     * 
+     * @return OpenID Connect Provider metadata
+     */
+    @Nullable public OIDCProviderMetadata getoIDCProviderMetadata() {
+        return oIDCProviderMetadata;
+    }
+
+    /**
+     * Set OpenID Connect Provider metatadata.
+     * 
+     * @param oIDCPrvdrMtdt OpenID Connect Provider metadata, must not be <code>null</code>.
+     */
+    public void setoIDCProviderMetadata(@Nonnull final OIDCProviderMetadata oIDCPrvdrMtdt) {
+        oIDCProviderMetadata = Constraint.isNotNull(oIDCPrvdrMtdt, "OpenID Connect metadata location cannot be null ");
+    }
+
+    /**
+     * Returns the oidc authentication request URI to be used for authentication.
+     * 
+     * @return request URI for authentication
+     */
+    @Nullable public URI getAuthenticationRequestURI() {
+        return authenticationRequestURI;
+    }
+
+    /**
+     * Set the oidc provider request URI for authentication.
+     * 
+     * @param request to be used for authentication, must not be <code>null</code>.
+     */
+    public void setAuthenticationRequestURI(@Nonnull final URI request) {
+        authenticationRequestURI = Constraint.isNotNull(request, 
+                "Authentication Request URI request can not be null");      
+
+    }
+
+    /**
+     * Returns the OIDC token response or null.
+     * 
+     * @return token response
+     */
+    @Nullable public OIDCTokenResponse getOidcTokenResponse() {
+        return oidcTknResponse;
+    }
+
+    /**
+     * Sets both the token response and the <code>idToken</code> from the
+     * token response if it is not <code>null</code>.
+     * 
+     * @param oidcTokenResponse response from provider
+     */
+    public void setOidcTokenResponse(@Nullable final OIDCTokenResponse oidcTokenResponse) {
+        oidcTknResponse = oidcTokenResponse;
+        if (oidcTokenResponse != null && oidcTokenResponse.getOIDCTokens() != null) {
+            idToken = oidcTokenResponse.getOIDCTokens().getIDToken();
+        }
+
+    }
+
+    /**
+     * Getter for State parameter.
+     * 
+     * @return state parameter
+     */
+    @Nullable public State getState() {
+        return state;
+    }
+
+    /**
+     * Setter for State parameter.
+     * 
+     * @param stateParam parameter
+     */
+    public void setState(@Nullable final State stateParam) {
+        state = stateParam;
+    }
+
+    /**
+     * Getter for Nonce parameter.
+     * 
+     * @return nonce parameter.
+     */
+    @Nullable public Nonce getNonce() {
+        return nonce;
+    }
+
+    /**
+     * Setter for Nonce parameter.
+     * 
+     * @param newNonce nonce parameter
+     */
+    public void setNonce(@Nullable final Nonce newNonce) {
+        nonce = newNonce;
+    }
+
+    /**
+     * Returns the authentication success response or null.
+     * 
+     * @return authentication success response.
+     */
+    @Nullable public AuthenticationSuccessResponse getAuthenticationSuccessResponse() {
+        return authSuccessResponse;
+    }
+
+    /**
+     * Sets the authentication success response.
+     * 
+     * @param authenticationSuccessResponse response from the OpenIDConenct Provider
+     */
+    public void setAuthenticationSuccessResponse(
+            @Nonnull final AuthenticationSuccessResponse authenticationSuccessResponse) {
+        authSuccessResponse = Constraint.isNotNull(authenticationSuccessResponse, 
+                "AuthenticationSuccessResponse can not be null");      
+
+    }
+
+    /**
+     * Returns the authentication response URI or null.
+     * 
+     * @return authentication response URI
+     */
+    @Nullable public URI getAuthenticationResponseURI() {
+        return authenticationResponseURI;
+    }
+
+    /**
+     * Parses the authentication response URI from the authenticationResponseHttpRequest.
+     * 
+     * @param authenticationResponseHttpRequest request
+     * 
+     * @throws URISyntaxException if request has malformed URL and/or query parameters
+     */
+    public void setAuthenticationResponseURI(@Nonnull final HttpServletRequest authenticationResponseHttpRequest)
+            throws URISyntaxException {
+
+        Constraint.isNotNull(authenticationResponseHttpRequest, "Authentication response http request can not be null");
+        
+        final String temp = authenticationResponseHttpRequest.getRequestURL() + "?"
+                + authenticationResponseHttpRequest.getQueryString();
+        authenticationResponseURI = new URI(temp);
+
+    }
+
+  
+
+}
diff --git a/java-idp-oidc-rp-dist/pom.xml b/java-idp-oidc-rp-dist/pom.xml
new file mode 100644
index 0000000..09e8708
--- /dev/null
+++ b/java-idp-oidc-rp-dist/pom.xml
@@ -0,0 +1,51 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>net.shibboleth.idp.plugin.authn</groupId>
+        <artifactId>idp-plugin-oidc-rp-parent</artifactId>
+        <version>0.0.1-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>idp-plugin-oidc-rp-dist</artifactId>
+    <name>Shibboleth IdP :: Plugins :: OIDC RP Login Flow Distribution</name>
+    <description>IdP java-idp-oidc-rp plugin packaging.</description>
+    <packaging>pom</packaging>
+
+
+    <properties>
+        <checkstyle.configLocation>${project.basedir}/../checkstyle.xml</checkstyle.configLocation>
+        <dist.plugin.finalName>idp-plugin-java-idp-oidc-rp-${project.version}</dist.plugin.finalName>
+    </properties>
+
+    <build>
+        <plugins>
+            <!-- Assemble -->
+            <plugin>
+                <artifactId>maven-assembly-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>make-assembly</id>
+                        <phase>package</phase>
+                        <goals>
+                            <goal>single</goal>
+                        </goals>
+                        <configuration>
+                            <appendAssemblyId>false</appendAssemblyId>
+                            <descriptors>
+                                <descriptor>src/main/assembly/assembly-tgz.xml</descriptor>
+                                 <descriptor>src/main/assembly/assembly-zip.xml</descriptor>
+                            </descriptors>
+                            <finalName>${dist.plugin.finalName}</finalName>
+                            <tarLongFileMode>gnu</tarLongFileMode>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+
+    </build>
+
+
+</project>
\ No newline at end of file
diff --git a/java-idp-oidc-rp-dist/src/main/assembly/assembly-tgz.xml b/java-idp-oidc-rp-dist/src/main/assembly/assembly-tgz.xml
new file mode 100644
index 0000000..679e359
--- /dev/null
+++ b/java-idp-oidc-rp-dist/src/main/assembly/assembly-tgz.xml
@@ -0,0 +1,72 @@
+<assembly
+	xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2"
+	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd">
+	<id>bin</id>
+	<formats>
+		<format>tar.gz</format>
+	</formats>
+
+	<fileSets>
+		<fileSet>
+			<directory>src/main/resources/</directory>
+			<outputDirectory></outputDirectory>
+		</fileSet>
+
+        <!-- The plugin property file -->
+		<fileSet>			
+			<directory>../java-idp-oidc-rp-impl/target/classes/net/shibboleth/idp/plugin/authn/oidc/rp/</directory>
+			<outputDirectory>bootstrap</outputDirectory>
+			<includes>
+				<include>plugin.properties</include>
+			</includes>
+		</fileSet>
+		
+		<!--  The implementation jar -->
+        <fileSet>
+            <directory>../java-idp-oidc-rp-impl/target</directory>
+            <outputDirectory>webapp/WEB-INF/lib</outputDirectory>
+            <includes>
+                <include>idp-plugin-oidc-rp-impl-*.jar</include>
+            </includes>
+            <excludes>
+                <exclude>*test*.jar</exclude>
+                <exclude>*javadoc.jar</exclude>
+                <exclude>*sources.jar</exclude>
+            </excludes>
+        </fileSet>
+        
+        <!--  The API jar -->
+        <fileSet>
+            <directory>../java-idp-oidc-rp-api/target</directory>
+            <outputDirectory>webapp/WEB-INF/lib</outputDirectory>
+            <includes>
+                <include>idp-plugin-oidc-rp-api-*.jar</include>
+            </includes>
+            <excludes>
+                <exclude>*test*.jar</exclude>
+                <exclude>*javadoc.jar</exclude>
+                <exclude>*sources.jar</exclude>
+            </excludes>
+        </fileSet>
+        
+         <!--  The dependencies -->
+        <fileSet>
+            <directory>../idp-plugin-oidc-rp-impl/target/dependency</directory>
+            <outputDirectory>webapp/WEB-INF/lib</outputDirectory>
+            <includes>
+                <include>*.jar</include>
+            </includes>
+        </fileSet>
+
+        <!-- The signing certificates -->
+		<fileSet>			
+			<directory>src/main/resources/bootstrap</directory>
+			<outputDirectory>bootstrap</outputDirectory>
+			<includes>
+				<include>keys.txt</include>
+			</includes>
+		</fileSet>
+	</fileSets>
+
+</assembly>
diff --git a/java-idp-oidc-rp-dist/src/main/assembly/assembly-zip.xml b/java-idp-oidc-rp-dist/src/main/assembly/assembly-zip.xml
new file mode 100644
index 0000000..ac4aa21
--- /dev/null
+++ b/java-idp-oidc-rp-dist/src/main/assembly/assembly-zip.xml
@@ -0,0 +1,72 @@
+<assembly
+	xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2"
+	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd">
+	<id>bin</id>
+	<formats>
+		<format>zip</format>
+	</formats>
+
+	<fileSets>
+		<fileSet>
+			<directory>src/main/resources/</directory>
+			<outputDirectory></outputDirectory>
+		</fileSet>
+
+        <!-- The plugin property file -->
+		<fileSet>			
+			<directory>../java-idp-oidc-rp-impl/target/classes/net/shibboleth/idp/plugin/authn/oidc/rp/</directory>
+			<outputDirectory>bootstrap</outputDirectory>
+			<includes>
+				<include>plugin.properties</include>
+			</includes>
+		</fileSet>
+		
+		<!--  The implementation jar -->
+        <fileSet>
+            <directory>../java-idp-oidc-rp-impl/target</directory>
+            <outputDirectory>webapp/WEB-INF/lib</outputDirectory>
+            <includes>
+                <include>idp-plugin-oidc-rp-impl-*.jar</include>
+            </includes>
+            <excludes>
+                <exclude>*test*.jar</exclude>
+                <exclude>*javadoc.jar</exclude>
+                <exclude>*sources.jar</exclude>
+            </excludes>
+        </fileSet>
+        
+        <!--  The API jar -->
+        <fileSet>
+            <directory>../java-idp-oidc-rp-api/target</directory>
+            <outputDirectory>webapp/WEB-INF/lib</outputDirectory>
+            <includes>
+                <include>idp-plugin-oidc-rp-api-*.jar</include>
+            </includes>
+            <excludes>
+                <exclude>*test*.jar</exclude>
+                <exclude>*javadoc.jar</exclude>
+                <exclude>*sources.jar</exclude>
+            </excludes>
+        </fileSet>
+        
+         <!--  The dependencies -->
+        <fileSet>
+            <directory>../java-idp-oidc-rp-impl/target/dependency</directory>
+            <outputDirectory>webapp/WEB-INF/lib</outputDirectory>
+            <includes>
+                <include>*.jar</include>
+            </includes>
+        </fileSet>
+
+        <!-- The signing certificates -->
+		<fileSet>			
+			<directory>src/main/resources/bootstrap</directory>
+			<outputDirectory>bootstrap</outputDirectory>
+			<includes>
+				<include>keys.txt</include>
+			</includes>
+		</fileSet>
+	</fileSets>
+
+</assembly>
diff --git a/java-idp-oidc-rp-dist/src/main/resources/bootstrap/keys.txt b/java-idp-oidc-rp-dist/src/main/resources/bootstrap/keys.txt
new file mode 100644
index 0000000..921c030
--- /dev/null
+++ b/java-idp-oidc-rp-dist/src/main/resources/bootstrap/keys.txt
@@ -0,0 +1 @@
+FIXME: Plugin keys
\ No newline at end of file
diff --git a/java-idp-oidc-rp-impl/.checkstyle b/java-idp-oidc-rp-impl/.checkstyle
new file mode 100644
index 0000000..162e469
--- /dev/null
+++ b/java-idp-oidc-rp-impl/.checkstyle
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<fileset-config file-format-version="1.2.0" simple-config="true" sync-formatter="false">
+  <fileset name="all" enabled="true" check-config-name="shibboleth" local="false">
+    <file-match-pattern match-pattern="." include-pattern="true"/>
+  </fileset>
+</fileset-config>
diff --git a/java-idp-oidc-rp-impl/pom.xml b/java-idp-oidc-rp-impl/pom.xml
new file mode 100644
index 0000000..6f2cd13
--- /dev/null
+++ b/java-idp-oidc-rp-impl/pom.xml
@@ -0,0 +1,118 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>net.shibboleth.idp.plugin.authn</groupId>
+        <artifactId>idp-plugin-oidc-rp-parent</artifactId>
+        <version>0.0.1-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>idp-plugin-oidc-rp-impl</artifactId>
+    <packaging>jar</packaging>    
+    <name>Shibboleth IdP :: Plugins :: OIDC RP Login Flow Impl</name>
+    <description>IdP java-idp-oidc-rp plugin implementation.</description>
+    
+    <properties>
+        <checkstyle.configLocation>${project.basedir}/../checkstyle.xml</checkstyle.configLocation>
+        <automatic.module.name>net.shibboleth.idp.plugin.authn.oidc.rp.impl</automatic.module.name>
+    </properties>
+    
+    <dependencies>
+        
+         <!-- compile time intra project dependencies -->
+        <dependency>
+            <groupId>net.shibboleth.idp.plugin.authn</groupId>
+            <artifactId>idp-plugin-oidc-rp-api</artifactId>
+        </dependency>
+         <!-- provided dependencies -->
+         <dependency>
+            <groupId>com.nimbusds</groupId>
+            <artifactId>oauth2-oidc-sdk</artifactId>
+            <scope>provided</scope>
+        </dependency>    
+        <dependency>
+            <groupId>com.google.code.findbugs</groupId>
+            <artifactId>jsr305</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>net.shibboleth.utilities</groupId>
+            <artifactId>java-support</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.httpcomponents</groupId>
+            <artifactId>httpcore</artifactId>
+            <scope>provided</scope>
+        </dependency>
+         <dependency>
+            <groupId>com.nimbusds</groupId>
+            <artifactId>nimbus-jose-jwt</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>net.shibboleth.oidc</groupId>
+            <artifactId>oidc-common-crypto-impl</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>javax.servlet-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <!-- Service API and Plugin Description dependency -->
+        <dependency>
+            <groupId>${idp.groupId}</groupId>
+            <artifactId>idp-admin-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>${idp.groupId}</groupId>
+            <artifactId>idp-admin-impl</artifactId>
+            <scope>provided</scope>
+        </dependency>    
+    </dependencies>
+
+    <build>
+        <plugins>            
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-jar-plugin</artifactId>
+                <configuration>
+                    <archive>
+                        <manifestSections>
+                            <manifestSection>
+                                <name>org/example/com/java-idp-oidc-rp/</name>
+                                <manifestEntries>
+                                    <Implementation-Title>${project.artifactId}</Implementation-Title>
+                                    <Implementation-Version>${project.version}</Implementation-Version>
+                                    <Implementation-Vendor>Shibboleth</Implementation-Vendor>
+                                </manifestEntries>
+                            </manifestSection>
+                        </manifestSections>
+                    </archive>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-dependency-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>copy-dependencies-test</id>
+                        <phase>prepare-package</phase>
+                        <goals>
+                            <goal>copy-dependencies</goal>
+                        </goals>
+                        <configuration>
+                            <outputDirectory>${project.target.directory}</outputDirectory>
+                            <includeScope>runtime</includeScope>
+                            <excludeTransitive>true</excludeTransitive>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
\ No newline at end of file
diff --git a/java-idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/OIDCRPModule.java b/java-idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/OIDCRPModule.java
new file mode 100644
index 0000000..ecff9a9
--- /dev/null
+++ b/java-idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/OIDCRPModule.java
@@ -0,0 +1,25 @@
+package net.shibboleth.idp.plugin.authn.oidc.rp;
+
+import java.io.IOException;
+
+import net.shibboleth.idp.module.ModuleException;
+import net.shibboleth.idp.module.impl.PluginIdPModule;
+
+
+/**
+ * {@link IdPModule IdP Module} implementation.
+ */
+public class OIDCRPModule extends PluginIdPModule{
+    
+    /**
+     * Constructor.
+     *  
+     * @throws ModuleException on error
+     * @throws IOException on error
+     */
+    public OIDCRPModule() throws IOException, ModuleException {
+        super(OIDCRPModule.class);
+    }
+    
+
+}
\ No newline at end of file
diff --git a/java-idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/OIDCRPPlugin.java b/java-idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/OIDCRPPlugin.java
new file mode 100644
index 0000000..27fd2c8
--- /dev/null
+++ b/java-idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/OIDCRPPlugin.java
@@ -0,0 +1,27 @@
+package net.shibboleth.idp.plugin.authn.oidc.rp;
+
+import java.io.IOException;
+import javax.annotation.Nonnull;
+
+import net.shibboleth.idp.plugin.IdPPlugin;
+import net.shibboleth.idp.plugin.PluginException;
+import net.shibboleth.idp.plugin.PropertyDrivenIdPPlugin;
+
+/**
+ * Plugin description about the webauthn plugin.
+ */
+public class OIDCRPPlugin extends PropertyDrivenIdPPlugin {
+
+    /**
+     * Constructor.
+     *
+     * @param claz type of plugin
+     * 
+     * @throws IOException if properties can't be loaded
+     * @throws PluginException if another error occurs
+     */
+    public OIDCRPPlugin(@Nonnull final Class<? extends IdPPlugin> claz) throws IOException, PluginException {
+        super(claz);
+    }
+
+}
\ No newline at end of file
diff --git a/java-idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/AuthorizationController.java b/java-idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/AuthorizationController.java
new file mode 100644
index 0000000..bb9fd43
--- /dev/null
+++ b/java-idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/AuthorizationController.java
@@ -0,0 +1,158 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.idp.plugin.authn.oidc.rp.impl;
+
+import java.io.IOException;
+import java.net.URISyntaxException;
+
+import javax.annotation.Nonnull;
+import javax.annotation.concurrent.ThreadSafe;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+
+import net.shibboleth.idp.authn.ExternalAuthentication;
+import net.shibboleth.idp.authn.ExternalAuthenticationException;
+import net.shibboleth.idp.authn.context.AuthenticationContext;
+import net.shibboleth.idp.plugin.authn.oidc.rp.context.OpenIDConnectContext;
+import net.shibboleth.utilities.java.support.primitive.StringSupport;
+
+/**
+ * 
+ * Servlet compatible with the {@link ExternalAuthentication} interface that begins, by HTTP redirect, an 
+ * OpenID Connect authentication request via OAuth 2.0 Authorization Code Flow to an OpenID Connect 
+ * Identity Provider (an OAuth 2.0 Authorization Server that supports OpenID Connect). 
+ * Although other flows could be configured, they shouldn't.
+ * 
+ * <p>As this is part of an OpenID Connect authentication request, the <code>openid</code> scope is required, 
+ * and as such is automatically set by the {@link SetOIDCInformation} action.</p>
+ * 
+ * <p>Adds the {@link OpenIDConnectContext} to the {@link HttpSession} for extraction after the OAuth 2.0 
+ * authorisation code response has been consumed, and control as returned to the IdP.</p>
+ * 
+ * <p>Servlet compatible with the @link {@link ExternalAuthentication} interface that consumes the HTTP Request from 
+ * an OpenID Connect provider (OAuth 2.0 authorization endpoint), which was issued in response to the Authorization 
+ * Code request made by the {@link OpenIDConnectStartServlet}. </p>
+ * <p>
+ * The {@link HttpServletRequest} is placed inside the {@link OpenIDConnectContext} for interrogation later in the flow.
+ * </p>
+ * 
+ * @since 4.0.0
+ */
+ at ThreadSafe
+ at Controller
+ at RequestMapping("/Authn/OIDC/RP")
+public class AuthorizationController {
+
+    /** Prefix for the session attribute ids. */
+    @Nonnull public static final String SESSION_ATTR_PREFIX =
+            "net.shibboleth.idp.authn.oidc.impl.OpenIdConnectStartServlet.";
+
+    /** Session attribute id for flow conversation key. */
+    @Nonnull public static final String SESSION_ATTR_FLOWKEY = SESSION_ATTR_PREFIX + "key";
+
+    /** Session attribute id for {@link OpenIDConnectContext}. */
+    @Nonnull public static final String SESSION_ATTR_SUCTX = SESSION_ATTR_PREFIX + "openIdConnectContext";
+
+    /** Serial UID. */
+    private static final long serialVersionUID = -3162157736238514852L;
+
+    /** Class logger. */
+    @Nonnull private final Logger log = LoggerFactory.getLogger(AuthorizationController.class);
+
+
+    @GetMapping("/auth")
+    public void authorizationRequest(@Nonnull final HttpServletRequest httpRequest, 
+            @Nonnull final HttpServletResponse httpResponse) throws ServletException, IOException {
+       
+        try {
+            final String key = ExternalAuthentication.startExternalAuthentication(httpRequest);
+            httpRequest.getSession().setAttribute(SESSION_ATTR_FLOWKEY, key);
+
+            @SuppressWarnings("rawtypes") final ProfileRequestContext profileRequestContext =
+                    (ProfileRequestContext) httpRequest.getAttribute(ProfileRequestContext.BINDING_KEY);
+            if (profileRequestContext == null) {
+                throw new ExternalAuthenticationException("Could not access profileRequestContext from the request");
+            }
+            final AuthenticationContext authenticationContext =
+                    (AuthenticationContext) profileRequestContext.getSubcontext(AuthenticationContext.class);
+            if (authenticationContext == null) {
+                throw new ExternalAuthenticationException("Could not get AuthenticationContext from the request");
+            }
+            final OpenIDConnectContext openIDConnectContext =
+                    (OpenIDConnectContext) authenticationContext
+                            .getSubcontext(OpenIDConnectContext.class);
+            if (openIDConnectContext == null) {
+                throw new ExternalAuthenticationException(
+                        "Could not get OpenIdConnectContext from the request");
+            }
+            //TODO do not put state in session, put in the request.
+            httpRequest.getSession().setAttribute(SESSION_ATTR_SUCTX, openIDConnectContext);
+            log.debug("Redirecting http-agent to {}", openIDConnectContext.getAuthenticationRequestURI());
+            httpResponse.sendRedirect(openIDConnectContext.getAuthenticationRequestURI().toString());
+        } catch (final ExternalAuthenticationException e) {
+            log.error("Error processing external authentication request", e);           
+            throw new ServletException("Error processing external authentication request", e);
+        }
+       
+    }
+    
+    @GetMapping("/callback")
+    public void authorizationCallback(@Nonnull final HttpServletRequest httpRequest,
+            @Nonnull final HttpServletResponse httpResponse) throws ServletException, IOException {
+       
+        try {
+            final HttpSession session = httpRequest.getSession();
+            if (session == null) {
+                throw new ExternalAuthenticationException("No session exists, this URL shouldn't be called directly");
+            }
+            final String key = StringSupport.trimOrNull((String) httpRequest.getSession()
+                    .getAttribute(AuthorizationController.SESSION_ATTR_FLOWKEY));
+            if (key == null) {
+                throw new ExternalAuthenticationException(
+                        "Could not find value for " + AuthorizationController.SESSION_ATTR_FLOWKEY);
+            }
+            final OpenIDConnectContext openIDConnectContext =
+                    (OpenIDConnectContext) httpRequest.getSession()
+                            .getAttribute(AuthorizationController.SESSION_ATTR_SUCTX);
+            if (openIDConnectContext == null) {
+                throw new ExternalAuthenticationException(
+                        "Could not find value for " + AuthorizationController.SESSION_ATTR_SUCTX);
+            }
+            log.trace("Attempting URL {}?{}", httpRequest.getRequestURL(), httpRequest.getQueryString());
+            try {
+                openIDConnectContext.setAuthenticationResponseURI(httpRequest);
+            } catch (final URISyntaxException e) {
+                throw new ExternalAuthenticationException("Could not parse response URI", e);
+            }
+            ExternalAuthentication.finishExternalAuthentication(key, httpRequest, httpResponse);
+        } catch (final ExternalAuthenticationException e) {
+            log.error("Could not finish the external authentication", e);          
+            throw new ServletException("Error finishing the external authentication", e);
+        }
+        
+    }
+}
diff --git a/java-idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/GetOIDCTokenResponse.java b/java-idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/GetOIDCTokenResponse.java
new file mode 100644
index 0000000..d4b047d
--- /dev/null
+++ b/java-idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/GetOIDCTokenResponse.java
@@ -0,0 +1,137 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.idp.plugin.authn.oidc.rp.impl;
+
+import java.io.IOException;
+
+import javax.annotation.Nonnull;
+
+import net.shibboleth.idp.authn.AbstractExtractionAction;
+import net.shibboleth.idp.authn.AuthnEventIds;
+import net.shibboleth.idp.authn.context.AuthenticationContext;
+import net.shibboleth.idp.plugin.authn.oidc.rp.context.OpenIDConnectContext;
+
+import org.opensaml.profile.action.ActionSupport;
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.nimbusds.oauth2.sdk.AuthorizationCode;
+import com.nimbusds.oauth2.sdk.AuthorizationCodeGrant;
+import com.nimbusds.oauth2.sdk.AuthorizationGrant;
+import com.nimbusds.oauth2.sdk.ParseException;
+import com.nimbusds.oauth2.sdk.SerializeException;
+import com.nimbusds.oauth2.sdk.TokenErrorResponse;
+import com.nimbusds.oauth2.sdk.TokenRequest;
+import com.nimbusds.oauth2.sdk.TokenResponse;
+import com.nimbusds.oauth2.sdk.auth.ClientAuthentication;
+import com.nimbusds.oauth2.sdk.auth.ClientSecretBasic;
+import com.nimbusds.openid.connect.sdk.AuthenticationSuccessResponse;
+import com.nimbusds.openid.connect.sdk.OIDCTokenResponse;
+import com.nimbusds.openid.connect.sdk.OIDCTokenResponseParser;
+
+/**
+ * An action that exchanges the OAuth 2.0 authorization code inside the {@link OpenIDConnectContext} for
+ * an id_token from the OpenID Connect Provider's Token Endpoint. The ID Token is placed inside the 
+ * {@link OpenIDConnectContext}.
+ * 
+ * 
+ * @pre <pre>ProfileRequestContext.getSubcontext(AuthenticationContext.class, false) != null</pre>
+ * @pre <pre>AuthenticationContext.getSubcontext(OpenIDConnectContext.class, false) != null</pre>
+ * @post If getIDToken() !=null the method returns immediately. Otherwise, if the token endpoint returns an 
+ * {@link OIDCTokenResponse} whose indicatesSuccess()==true, the token is attached to the {@link OpenIDConnectContext}.
+ * @event {@link org.opensaml.profile.action.EventIds#PROCEED_EVENT_ID}
+ * @event {@link AuthnEventIds#NO_CREDENTIALS}
+ * @event {@link AuthnEventIds#INVALID_CREDENTIALS}
+ * 
+ * @since 4.0.0
+ */
+public class GetOIDCTokenResponse extends AbstractExtractionAction {
+
+    /** Class logger. */
+    @Nonnull private final Logger log = LoggerFactory.getLogger(GetOIDCTokenResponse.class);
+
+    /** {@inheritDoc} */
+    @Override
+    protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext, 
+            @Nonnull final AuthenticationContext authenticationContext) {
+        
+        final OpenIDConnectContext oidcCtx =
+                authenticationContext.getSubcontext(OpenIDConnectContext.class);
+        if (oidcCtx == null) {
+            log.error("{} Unable to find OIDC context", getLogPrefix());
+            ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.NO_CREDENTIALS);
+            
+            return;
+        }
+        if (oidcCtx.getIDToken() != null) {
+            log.debug("id_token already exists, nothing to fetch from token endpoint");
+            
+            return;
+        }
+        final AuthenticationSuccessResponse response = oidcCtx.getAuthenticationSuccessResponse();
+        
+        if (response == null) {
+            log.info("{} Authentication success response not found in OpenIDConnectContext", getLogPrefix());
+            ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.NO_CREDENTIALS);
+           
+            return;
+        }
+        final AuthorizationCode code = response.getAuthorizationCode();
+        //TODO P.S. code could be null, should not be.
+        final AuthorizationGrant codeGrant = new AuthorizationCodeGrant(code, oidcCtx.getRedirectURI());
+        //TODO P.S. neither should be null, both could be (although not after SetOIDCInformation initilises the context)
+        final ClientAuthentication clientAuth = new ClientSecretBasic(oidcCtx.getClientID(), oidcCtx.getClientSecret());
+        
+        log.trace("{} Using the following OIDC token endpoint URI: {}", getLogPrefix(),
+                oidcCtx.getoIDCProviderMetadata().getTokenEndpointURI());
+        
+        final TokenRequest tokenRequest =
+                new TokenRequest(oidcCtx.getoIDCProviderMetadata().getTokenEndpointURI(), clientAuth, codeGrant);
+        final OIDCTokenResponse oidcTokenResponse;
+        try {
+            final TokenResponse tokenResponse = OIDCTokenResponseParser.parse(tokenRequest.toHTTPRequest().send());
+            // TokenResponse can only be TokenErrorResponse or OIDCTokenResponse
+            if (tokenResponse instanceof OIDCTokenResponse) {
+                
+                oidcTokenResponse = (OIDCTokenResponse) tokenResponse;                
+                oidcCtx.setOidcTokenResponse(oidcTokenResponse);
+                
+                if (log.isTraceEnabled() && oidcTokenResponse.getOIDCTokens().getIDToken() != null) {
+                    log.trace("{} Retrieved id_token '{}'",getLogPrefix(),
+                            oidcTokenResponse.getOIDCTokens().getIDToken().getParsedString());
+                }
+                
+            } else {
+                final TokenErrorResponse errorResponse = (TokenErrorResponse) tokenResponse;
+                log.warn("{} Error in retrieving id_token, response error is {}", getLogPrefix(),
+                        errorResponse.getErrorObject());
+                // should map error object OAuth2 Error types to new or existing event ids.
+                ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.INVALID_CREDENTIALS);
+                return;
+            }
+
+        } catch (final SerializeException | IOException | ParseException e) {
+            log.error("{} token exchange failed", getLogPrefix(), e);
+            ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.INVALID_CREDENTIALS);           
+            return;
+        }
+
+       
+    }
+}
diff --git a/java-idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/SetOIDCInformation.java b/java-idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/SetOIDCInformation.java
new file mode 100644
index 0000000..7f10122
--- /dev/null
+++ b/java-idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/SetOIDCInformation.java
@@ -0,0 +1,583 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.idp.plugin.authn.oidc.rp.impl;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.security.PrivateKey;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Function;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import net.minidev.json.JSONObject;
+import net.shibboleth.idp.authn.AbstractAuthenticationAction;
+import net.shibboleth.idp.authn.AuthnEventIds;
+import net.shibboleth.idp.authn.context.AuthenticationContext;
+import net.shibboleth.idp.plugin.authn.oidc.rp.context.OpenIDConnectContext;
+
+import org.opensaml.messaging.context.navigate.ChildContextLookup;
+import org.opensaml.profile.action.ActionSupport;
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.nimbusds.jose.crypto.RSASSASigner;
+import com.nimbusds.jwt.JWT;
+import com.nimbusds.jwt.JWTClaimsSet;
+import com.nimbusds.jwt.PlainJWT;
+import com.nimbusds.oauth2.sdk.ParseException;
+import com.nimbusds.oauth2.sdk.ResponseMode;
+import com.nimbusds.oauth2.sdk.ResponseType;
+import com.nimbusds.oauth2.sdk.Scope;
+import com.nimbusds.oauth2.sdk.auth.Secret;
+import com.nimbusds.oauth2.sdk.id.ClientID;
+import com.nimbusds.oauth2.sdk.id.State;
+import com.nimbusds.openid.connect.sdk.AuthenticationRequest;
+import com.nimbusds.openid.connect.sdk.Display;
+import com.nimbusds.openid.connect.sdk.Nonce;
+import com.nimbusds.openid.connect.sdk.OIDCScopeValue;
+import com.nimbusds.openid.connect.sdk.Prompt;
+import com.nimbusds.openid.connect.sdk.Prompt.Type;
+import com.nimbusds.openid.connect.sdk.claims.ACR;
+import com.nimbusds.openid.connect.sdk.op.OIDCProviderMetadata;
+import com.nimbusds.jose.JWSAlgorithm;
+import com.nimbusds.jose.JWSHeader;
+import com.nimbusds.jose.PlainHeader;
+import com.nimbusds.jwt.SignedJWT;
+
+import net.shibboleth.idp.attribute.IdPAttribute;
+import net.shibboleth.idp.attribute.IdPAttributeValue;
+import net.shibboleth.idp.attribute.StringAttributeValue;
+import net.shibboleth.idp.attribute.context.AttributeContext;
+import net.shibboleth.idp.profile.context.RelyingPartyContext;
+import net.shibboleth.utilities.java.support.annotation.constraint.NonnullElements;
+import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
+import net.shibboleth.utilities.java.support.component.ComponentSupport;
+import net.shibboleth.utilities.java.support.logic.Constraint;
+import net.shibboleth.utilities.java.support.primitive.StringSupport;
+
+/**
+ *
+ * An action that populates the {@link AuthenticationContext} with a freshly built {@link OpenIDConnectContext}.
+ *
+ * <p>A singleton instance of this class can be created and shared between authentication requests.</p>
+ *
+ * @event {@link org.opensaml.profile.action.EventIds#PROCEED_EVENT_ID}
+ * @event {@link AuthnEventIds#NO_CREDENTIALS}
+ * @pre <pre>ProfileRequestContext.getSubcontext(AuthenticationContext.class) != null</pre>
+ * @post The AuthenticationContext is modified as above.
+ *
+ * @since 4.0.0
+ */
+ at SuppressWarnings("rawtypes")
+public class SetOIDCInformation extends AbstractAuthenticationAction {
+
+    /** Class logger. */
+    @Nonnull private final Logger log = LoggerFactory.getLogger(SetOIDCInformation.class);
+
+    /** Context to look attributes for. */
+    @Nonnull private Function<ProfileRequestContext, AttributeContext> attributeContextLookupStrategy;
+
+    /** Redirect URI. */
+    @Nonnull private URI redirectURI;
+
+    /** Client Id. */
+    @Nonnull private ClientID clientID;
+
+    /** Client Secret. */
+    @Nonnull private Secret clientSecret;
+
+    /** Response type, default is code flow. */
+    @Nonnull private ResponseType responseType = new ResponseType(ResponseType.Value.CODE);
+
+    /** Scope. Must contain an openid scope.*/
+    @Nonnull private Scope scope = new Scope(OIDCScopeValue.OPENID);
+
+    /**
+     *  OIDC Prompt. Specifies whether the Authorization Server prompts
+     *  the End-User for reauthentication and consent.
+     */
+    @Nullable private Prompt prompt;
+
+    /** OIDC Authentication Class Reference values. */
+    @Nullable @NonnullElements private List<ACR> acrs;
+
+    /**
+     *  OIDC Display. Value that specifies how the Authorization Server
+     *  displays the authentication and consent user interface pages to the End-User.
+     */
+    @Nullable private Display display;
+
+    /** Private key for signing the request object. */
+    @Nullable private PrivateKey signPrvKey;
+
+    /** Algorithm used for signing the request object. Defaults to the required
+     * singing algorithm of RSA SHA-256*/
+    @Nonnull private JWSAlgorithm jwsAlgorithm = JWSAlgorithm.RS256;
+
+    /** key id for key used for signing the request object. */
+    @Nonnull private String keyID = "id";
+
+    /** Request object claims. */
+    @Nullable private Map<String, String> requestClaims;
+
+    /** OIDC provider metadata. */
+    @Nonnull private OIDCProviderMetadata oIDCProviderMetadata;
+
+    /** Constructor. */
+    public SetOIDCInformation() {
+        attributeContextLookupStrategy =
+                new ChildContextLookup<>(AttributeContext.class).compose(
+                        new ChildContextLookup<>(RelyingPartyContext.class));
+
+    }
+
+    /**
+     * Set the private key used for signing request object.
+     *
+     * @param key signing key
+     */
+    public void setPrivKey(@Nullable final PrivateKey key) {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+        signPrvKey = key;
+    }
+
+    /**
+     * Set the RSA algorithm used for signing the request object. Default is RS256.
+     *
+     * @param algorithm used for signing, must not be <code>null</code>.
+     */
+    public void setJwsAlgorithm(@Nonnull final JWSAlgorithm algorithm) {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+        jwsAlgorithm = Constraint.isNotNull(algorithm,"OpenID Connect JWS Request algorithm can not be null");
+
+    }
+
+    /**
+     * Set the key id of the key.
+     *
+     * @param id for the key, must not be <code>null</code>.
+     */
+    public void setKeyID(@Nonnull @NotEmpty final String id) {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+        keyID =Constraint.isNotNull(StringSupport.trimOrNull(id), "OpenID Connect key ID cannot be null");
+    }
+
+    /**
+     *
+     * If this is set (i.e. not <code>null</code>) request claims will be built as a
+     * JWT Request Object. See OpenID Connect Core section 6.1. If the <code>signPrvKey</code>
+     * is set, the JWT will also be signed.
+     *
+     * <p>The key of the mapping is the name of the requested claim. The value of the requested claim
+     * is either:
+     * <ul>
+     * <li>Null, if the claim is being requested in a default manor</li>
+     * <li><code>{"essential":true}</code> if the value is essential. The default is
+     * <code>{"essential":false}</code></li>
+     * <li>A specific value for the claim. This value must be a valid value for that claim.</li>
+     * <li>A specific set of values for the claim. These values must be valid value for that claim.</li>
+     * </ul>
+     * </p>
+     * <p>
+     * If used, the OpenID Connect Provider must support it, as specified in the
+     * <code>request_parameter_supported</code> parameter of the Providers discovery metadata.
+     *
+     * @param claims map of requested claims
+     */
+    public void setRequestClaims(@Nullable final Map<String, String> claims) {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+        requestClaims = claims;
+    }
+
+    /**
+     * Sets the response type. Default is code.
+     *
+     * @param type space-delimited list of one or more authorization response types. Must not be <code>null</code>.
+     * @throws ParseException if response type cannot be parsed
+     */
+    public void setResponseType(@Nonnull @NotEmpty final String type) throws ParseException {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+        Constraint.isNotNull(StringSupport.trimOrNull(type), "OpenID Connect response type cannot be null or empty");
+
+        responseType = ResponseType.parse(type);
+    }
+
+    /**
+     * Setter for Oauth2 client id.
+     *
+     * @param oauth2ClientID Oauth2 Client ID, must not be <code>null</code>.
+     */
+    public void setClientID(@Nonnull @NotEmpty final String oauth2ClientID) {
+
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+        Constraint.isNotNull(StringSupport.trimOrNull(oauth2ClientID),
+                "OpenID Connect client ID cannot be null or empty");
+
+        clientID = new ClientID(oauth2ClientID);
+    }
+
+    /**
+     * Setter for OAuth2 Client secret.
+     *
+     * @param oauth2ClientSecret OAuth2 Client Secret, must not be <code>null</code>.
+     */
+    public void setClientSecret(@Nonnull @NotEmpty final String oauth2ClientSecret) {
+
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+        Constraint.isNotNull(StringSupport.trimOrNull(oauth2ClientSecret),
+                "OpenID Connect client secret cannot be null or empty");
+
+        clientSecret = new Secret(oauth2ClientSecret);
+    }
+
+    /**
+     * Setter for the OAuth 2.0 redirect URI the OIDC provider will return to. The constructed URI should not be empty.
+     *
+     * @param redirect OAuth2 redirect uri, must not be <code>null</code>.
+     */
+
+    public void setRedirectURI(@Nonnull @NotEmpty final URI redirect) {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+        Constraint.isNotNull(redirect, "OpenID Connect redirect URI cannot be null");
+        Constraint.isNotEmpty(redirect.toString(), "OpenID Connect redirect URI cannot be empty");
+
+        redirectURI = redirect;
+
+    }
+
+    /**
+     * Setter for OpenId Provider Metadata resource.
+     *
+     * <p>Constructs a URI from the <code>metadataLocation</code> and attempts to connect,
+     * stream, and parse its content into an {@link OIDCProviderMetadata} instance.</p>
+     *
+     * @param metadataLocation OpenId Provider Metadata location, must not be <code>null</code>.
+     *
+     * @throws URISyntaxException if metadataLocation is not a URI
+     * @throws IOException if metadataLocation cannot be read
+     * @throws ParseException if metadataLocation has wrong content
+     */
+    //TODO: not like this? HttpClient, metadata service etc.
+    public void setProviderMetadataLocation(@Nonnull @NotEmpty final String metadataLocation)
+            throws URISyntaxException, IOException, ParseException {
+
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+        Constraint.isNotNull(StringSupport.trimOrNull(metadataLocation),
+                "OpenID Connect metadata location cannot be null or empty");
+
+        final URI issuerURI = new URI(metadataLocation);
+        final URL providerConfigurationURL = issuerURI.resolve(".well-known/openid-configuration").toURL();
+        final InputStream stream = providerConfigurationURL.openStream();
+        String providerInfo = null;
+        try (java.util.Scanner s = new java.util.Scanner(stream)) {
+            providerInfo = s.useDelimiter("\\A").hasNext() ? s.next() : "";
+        }
+        oIDCProviderMetadata = OIDCProviderMetadata.parse(providerInfo);
+
+    }
+
+    /**
+     * Setter for OpenId Scope values. New ones are be added to the {@value OIDCScopeValue#OPENID} scope.
+     *
+     * @param oidcScopes OpenID Connect Scope values, can be <code>null</code> and will be ignored.
+     */
+    public void setScope(@Nullable final List<String> oidcScopes) {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+
+        //As the OPENID scope is always set. New scopes can be null.
+        if (oidcScopes==null) {
+            return;
+        }
+
+        for (final String oidcScope : oidcScopes) {
+            switch (oidcScope.toUpperCase()) {
+                case "ADDRESS":
+                    scope.add(OIDCScopeValue.ADDRESS);
+                    break;
+                case "EMAIL":
+                    scope.add(OIDCScopeValue.EMAIL);
+                    break;
+                case "OFFLINE_ACCESS":
+                    scope.add(OIDCScopeValue.OFFLINE_ACCESS);
+                    break;
+                case "PHONE":
+                    scope.add(OIDCScopeValue.PHONE);
+                    break;
+                case "PROFILE":
+                    scope.add(OIDCScopeValue.PROFILE);
+                    break;
+                default:
+            }
+        }
+
+    }
+
+    /**
+     * Setter for the OpenID Connect Prompt value.
+     *
+     * @param oidcPrompt  OpenID Connect Prompt value.
+     */
+    public void setPrompt(@Nullable final String oidcPrompt) {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+
+        prompt = new Prompt(oidcPrompt);
+    }
+
+    /**
+     * Setter for the OpenID Connect Authentication Context Class Reference (ACR) values.
+     *
+     * @param oidcAcrs OpenId ACR values
+     */
+    public void setAcr(@Nullable @NonnullElements final List<String> oidcAcrs) {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+
+        if (null != oidcAcrs) {
+            for (final String oidcAcr : oidcAcrs) {
+                final ACR acr = new ACR(oidcAcr);
+
+                if (acrs == null) {
+                    acrs = new ArrayList<ACR>();
+                }
+                if (acr!=null) {
+                    acrs.add(acr);
+                }
+            }
+        }
+
+    }
+
+    /**
+     * Setter for the OpenID Connect Display value.
+     *
+     * @param oidcDisplay OpenID Connect Display value.
+     */
+    public void setDisplay(@Nullable final String oidcDisplay) {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+
+        if (null != display) {
+             try {
+                 display = Display.parse(oidcDisplay);
+             } catch (final ParseException e) {
+                 log.error("{} Could not set display value",getLogPrefix(), e);
+             }
+        }
+
+    }
+
+    /**
+     * Set the lookup strategy for the {@link AttributeContext}.
+     *
+     * @param strategy lookup strategy
+     */
+    public void setAttributeContextLookupStrategy(
+            @Nonnull final Function<ProfileRequestContext, AttributeContext> strategy) {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+
+        attributeContextLookupStrategy =
+                Constraint.isNotNull(strategy, "AttributeContext lookup strategy cannot be null");
+
+    }
+
+    /**
+     * Returns the first {@link StringAttributeValue} value of the {@link IdPAttribute} that has key
+     * <code>name</code> from the {@link OpenIDConnectContext#getResolvedIdPAttributes()}.
+     *
+     * @param oidcCtx context to get attributes from.
+     * @param name of the attribute to find.
+     * @return attribute value if found, null otherwise.
+     */
+    @Nullable private String attributeToString(@Nonnull final OpenIDConnectContext oidcCtx,
+            @Nullable final String name) {
+
+        if (oidcCtx.getResolvedIdPAttributes() == null) {
+            log.warn("Attribute context not available");
+            return null;
+        }
+        final IdPAttribute attribute = oidcCtx.getResolvedIdPAttributes().get(name);
+        if (attribute == null || attribute.getValues().size() == 0) {
+            log.debug("attribute " + name + " not found or has no values");
+            return null;
+        }
+        for (final IdPAttributeValue attrValue : attribute.getValues()) {
+            if (attrValue instanceof StringAttributeValue) {
+                // We set the value
+                return attrValue.getDisplayValue();
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Build the requested claims into an JSONObject, used by
+     * {@link #getRequestObject(OpenIDConnectContext, State)}.
+     *
+     * @param oidcCtx context to extract request claims from.
+     * @return id token as a JSON Object.
+     */
+    @Nonnull private JSONObject buildIDToken(@Nonnull final OpenIDConnectContext oidcCtx) {
+
+        final JSONObject idToken = new JSONObject();
+        for (final Map.Entry<String, String> entry : requestClaims.entrySet()) {
+            final String value = entry.getValue();
+            final String claim = entry.getKey();
+            if (value == null) {
+                // 1. null value
+                log.debug("Setting claim " + claim + " to null");
+                idToken.put(entry.getKey(), value);
+                continue;
+            }
+            log.debug("locating attribute for " + value);
+            final String attrValue = attributeToString(oidcCtx, value);
+            if (attrValue != null) {
+                // 2. attribute value
+                log.debug("Setting claim " + claim + " to value " + attrValue);
+                idToken.put(claim, attrValue);
+                continue;
+            }
+            if ("essential".equals(value)) {
+                // 3. essential value
+                final JSONObject obj = new JSONObject();
+                obj.put("essential", true);
+                log.debug("Setting claim " + claim + " to value " + obj.toJSONString());
+                idToken.put(claim, obj);
+                continue;
+            }
+            // 4. string value
+            log.debug("Setting claim " + claim + " to value " + value);
+            idToken.put(claim, value);
+        }
+
+        return idToken;
+    }
+
+    /**
+     *
+     * Construct a JWT claims Request Object (see OpenID Connect core 1.0 section 6) iff claims
+     * are requested i.e. <code>requestClaims</code> is not <code>null</code>.
+     *
+     * <p>If the signing key is present, adds also state, iat claims and then signs it.</p>
+     *
+     * <p>Must be called as a last step before constructing the request.</p>
+     *
+     *
+     * @param oidcCtx context for accessing attributes.
+     * @param state to be added to the request object.
+     * @return the request object, or null if one is not constructed.
+     *
+     * @throws Exception if attribute context is not available or parsing/signing fails.
+     */
+    @Nullable private JWT getRequestObject(@Nonnull final OpenIDConnectContext oidcCtx, @Nonnull final State state)
+            throws Exception {
+
+
+        if (requestClaims == null || requestClaims.size() == 0) {
+            return null;
+        }
+        final JSONObject request = new JSONObject();
+        request.put("client_id", clientID.getValue());
+        request.put("response_type", responseType.toString());
+        if (signPrvKey != null) {
+            request.put("iss", clientID.getValue());
+            request.put("aud", oIDCProviderMetadata.getIssuer().getValue());
+            // If we sign we add also iat and state.
+            request.put("state", state.getValue());
+            request.put("iat", TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()));
+        }
+        // Build the id token as instructed.
+        final JSONObject idToken = buildIDToken(oidcCtx);
+        final JSONObject claims = new JSONObject();
+        claims.put("id_token", idToken);
+        request.put("claims", claims);
+        log.debug("Request object without signature "+getLogPrefix() + request.toJSONString());
+        final JWTClaimsSet claimsRequest = JWTClaimsSet.parse(request);
+        JWT requestObject = null;
+        if (signPrvKey != null) {
+            requestObject = new SignedJWT(new JWSHeader.Builder(jwsAlgorithm).keyID(keyID).build(), claimsRequest);
+            ((SignedJWT) requestObject).sign(new RSASSASigner(signPrvKey));
+            log.debug("created request object: " + requestObject.getParsedString());
+        } else {
+            requestObject = new PlainJWT(new PlainHeader(), claimsRequest);
+        }
+        return requestObject;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext,
+            @Nonnull final AuthenticationContext authenticationContext) {
+
+
+        final OpenIDConnectContext oidcCtx =
+                authenticationContext.getSubcontext(OpenIDConnectContext.class, true);
+
+        // Initialize the context, if request is passive we override default prompt value
+        final Prompt ovrPrompt = authenticationContext.isPassive() ? new Prompt(Type.NONE) : prompt;
+        oidcCtx.setPrompt(ovrPrompt);
+        oidcCtx.setAcrs(acrs);
+        oidcCtx.setClientID(clientID);
+        oidcCtx.setClientSecret(clientSecret);
+        oidcCtx.setDisplay(display);
+        oidcCtx.setoIDCProviderMetadata(oIDCProviderMetadata);
+        oidcCtx.setRedirectURI(redirectURI);
+        final State state = new State();
+        oidcCtx.setState(state);
+        final Nonce nonce = new Nonce();
+        oidcCtx.setNonce(nonce);
+
+        JWT requestObject = null;
+        try {
+            // must be called as a last step
+            requestObject = getRequestObject(oidcCtx, state);
+        } catch (final Exception e) {
+            // TODO: better error id
+            log.error("{} unable to create request object", getLogPrefix());
+            ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.NO_CREDENTIALS);
+
+            return;
+        }
+        if (authenticationContext.isForceAuthn()) {
+            // We set max age to 0 if forcedauth is set
+            // TODO: Currently the underlying library doesn't accept value 0, so
+            // we set it to 1
+            final int maxAge = 1;
+            oidcCtx.setAuthenticationRequestURI(
+                    new AuthenticationRequest.Builder(responseType, scope, clientID, redirectURI)
+                            .endpointURI(oIDCProviderMetadata.getAuthorizationEndpointURI()).display(display)
+                            .acrValues(acrs).requestObject(requestObject).responseMode(ResponseMode.QUERY)
+                            .maxAge(maxAge).prompt(ovrPrompt).state(state).nonce(nonce).build().toURI());
+        } else {
+            oidcCtx.setAuthenticationRequestURI(
+                    new AuthenticationRequest.Builder(responseType, scope, clientID, redirectURI)
+                            .endpointURI(oIDCProviderMetadata.getAuthorizationEndpointURI()).display(display)
+                            .acrValues(acrs).requestObject(requestObject).responseMode(ResponseMode.QUERY)
+                            .prompt(ovrPrompt).state(state).nonce(nonce).build().toURI());
+        }
+
+        return;
+    }
+
+}
diff --git a/java-idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/ValidateIDTokenACR.java b/java-idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/ValidateIDTokenACR.java
new file mode 100644
index 0000000..f64e36e
--- /dev/null
+++ b/java-idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/ValidateIDTokenACR.java
@@ -0,0 +1,103 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.idp.plugin.authn.oidc.rp.impl;
+
+import java.text.ParseException;
+import java.util.List;
+
+import javax.annotation.Nonnull;
+
+import org.opensaml.profile.action.ActionSupport;
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.nimbusds.openid.connect.sdk.claims.ACR;
+
+import net.shibboleth.idp.authn.AbstractAuthenticationAction;
+import net.shibboleth.idp.authn.AuthnEventIds;
+import net.shibboleth.idp.authn.context.AuthenticationContext;
+import net.shibboleth.idp.plugin.authn.oidc.rp.context.OpenIDConnectContext;
+import net.shibboleth.utilities.java.support.primitive.StringSupport;
+
+/**
+ * An action that verifies the Authentication Context Class Reference (ACR) values contained within the 
+ * id_token matches those requested.
+ * 
+ * <p>ACR claims are optional</p>
+ * 
+ * @pre <pre>ProfileRequestContext.getSubcontext(AuthenticationContext.class, false) != null</pre>
+ * @pre <pre>AuthenticationContext.getSubcontext(OpenIDConnectContext.class, false) != null</pre>
+ * @event {@link net.shibboleth.idp.authn.AuthnEventIds#NO_CREDENTIALS}
+ * @event {@link org.opensaml.profile.action.EventIds#PROCEED_EVENT_ID}
+ * 
+ * @since 4.0.0
+ */
+//TODO P.S should some of these functions be delegated to Nimbus IDTokenValidator? 
+public class ValidateIDTokenACR extends AbstractAuthenticationAction {
+
+    /** Class logger. */
+    @Nonnull private final Logger log = LoggerFactory.getLogger(ValidateIDTokenACR.class);
+
+    /** {@inheritDoc} */
+    @Override
+    protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext,
+            @Nonnull final AuthenticationContext authenticationContext) {
+        
+
+        final OpenIDConnectContext oidcCtx =
+                authenticationContext.getSubcontext(OpenIDConnectContext.class);
+        if (oidcCtx == null) {
+            log.error("{} Unable to find OIDC context", getLogPrefix());
+            ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.NO_CREDENTIALS);
+            
+            return;
+        }
+
+        
+        final List<ACR> acrs = oidcCtx.getAcrs();
+        if (acrs != null && !acrs.isEmpty()) {
+            if (log.isTraceEnabled()) {
+                for (int i = 0; i < acrs.size(); i++) {
+                    log.trace("{} ACR index {} is {}", getLogPrefix(), i, acrs.get(i));
+                }
+            }
+            final String acr;
+            try {
+                //TODO P.S. this could be null.
+                acr = oidcCtx.getIDToken().getJWTClaimsSet().getStringClaim("acr");
+            } catch (final ParseException e) {
+                log.error("{} Error parsing id token", getLogPrefix());
+                ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.NO_CREDENTIALS);                
+                return;
+            }
+            if (StringSupport.trimOrNull(acr) == null) {
+                log.error("{} acr requested but not received", getLogPrefix());
+                ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.NO_CREDENTIALS);                
+                return;
+            }
+            if (!acrs.contains(new ACR(acr))) {
+                log.error("{} acr received does not match requested:" + acr, getLogPrefix());
+                ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.NO_CREDENTIALS);                
+                return;
+            }
+        }
+        
+        return;
+    }
+}
diff --git a/java-idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/ValidateIDTokenAudience.java b/java-idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/ValidateIDTokenAudience.java
new file mode 100644
index 0000000..91cb085
--- /dev/null
+++ b/java-idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/ValidateIDTokenAudience.java
@@ -0,0 +1,91 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.idp.plugin.authn.oidc.rp.impl;
+
+import java.text.ParseException;
+
+import javax.annotation.Nonnull;
+
+import net.shibboleth.idp.authn.AbstractAuthenticationAction;
+import net.shibboleth.idp.authn.AuthnEventIds;
+import net.shibboleth.idp.authn.context.AuthenticationContext;
+import net.shibboleth.idp.plugin.authn.oidc.rp.context.OpenIDConnectContext;
+
+import org.opensaml.profile.action.ActionSupport;
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * An action that verifies the Audience (aud) claim in the id_token contains the client_id of this
+ * client (as registered at the issuer).  See section 3.1.3.7 of OpenID Connect core 1.0.
+ * 
+ * <p>If one of the list of audiences is untrusted, the token should be rejected - which is 
+ * Currently NOT tested</p>
+ * 
+ * 
+ * @pre <pre>ProfileRequestContext.getSubcontext(AuthenticationContext.class, false) != null</pre>
+ * @pre <pre>AuthenticationContext.getSubcontext(OpenIDConnectContext.class, false) != null</pre>
+ * @pre <pre>OpenIDConnectContext.getIDToken() != null</pre>
+ * @event {@link org.opensaml.profile.action.EventIds#PROCEED_EVENT_ID} 
+ * @event {@link net.shibboleth.idp.authn.AuthnEventIds#NO_CREDENTIALS}
+ * 
+ * @since 4.0.0
+ */
+//TODO P.S should some of these functions be delegated to Nimbus IDTokenValidator?
+public class ValidateIDTokenAudience extends AbstractAuthenticationAction {
+
+    /** Class logger. */
+    @Nonnull private final Logger log = LoggerFactory.getLogger(ValidateIDTokenAudience.class);
+
+    /** {@inheritDoc} */
+    @Override
+    protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext,
+            @Nonnull final AuthenticationContext authenticationContext) {
+        
+
+        final OpenIDConnectContext oidcCtx =
+                authenticationContext.getSubcontext(OpenIDConnectContext.class);
+        if (oidcCtx == null) {
+            log.error("{} Not able to find oidc context", getLogPrefix());
+            ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.NO_CREDENTIALS);           
+            return;
+        }
+        if (oidcCtx.getIDToken() == null) {
+            log.error("{} Not able to find id token", getLogPrefix());
+            ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.NO_CREDENTIALS);            
+            return;
+        }
+
+        try {
+            //TODO P.S. getClientID could be null, although not after SetOIDCInformation has run.
+            if (!oidcCtx.getIDToken().getJWTClaimsSet().getAudience().contains(oidcCtx.getClientID().getValue())) {
+                log.error("{} client is not the intended audience", getLogPrefix());
+                ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.NO_CREDENTIALS);                
+                return;
+            }
+        } catch (final ParseException e) {
+            log.error("{} Error parsing id token", getLogPrefix());
+            ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.NO_CREDENTIALS);            
+            return;
+        }       
+        return;
+    }
+
+}
diff --git a/java-idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/ValidateIDTokenAuthenticationTime.java b/java-idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/ValidateIDTokenAuthenticationTime.java
new file mode 100644
index 0000000..260e694
--- /dev/null
+++ b/java-idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/ValidateIDTokenAuthenticationTime.java
@@ -0,0 +1,185 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.idp.plugin.authn.oidc.rp.impl;
+
+import java.text.ParseException;
+import java.util.Date;
+
+import javax.annotation.Nonnull;
+
+import net.shibboleth.idp.authn.AbstractAuthenticationAction;
+import net.shibboleth.idp.authn.AuthnEventIds;
+import net.shibboleth.idp.authn.context.AuthenticationContext;
+import net.shibboleth.idp.plugin.authn.oidc.rp.context.OpenIDConnectContext;
+import net.shibboleth.utilities.java.support.annotation.constraint.NonNegative;
+import net.shibboleth.utilities.java.support.component.ComponentSupport;
+import net.shibboleth.utilities.java.support.logic.Constraint;
+
+import org.joda.time.DateTime;
+import org.opensaml.profile.action.ActionSupport;
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * An action that checks if auth_time (when the End-User authentication took place) stored within the id_token
+ * is within a valid expiration window. That is:
+ * <ul>
+ * <li>The auth_time when the End-User authenticated should not be in the future. More specifically, now plus 
+ * some clock skew (default is 3 minutes)</li> * 
+ * <li>The authentication and hence id_token has not expired. More specifically, the expiration time 
+ * (the auth_time plus some clock skew plus the authnLifetime) should be after the current time.<li> 
+ * </ul>
+ * 
+ * <p>The auth_time is optional, and needs to be requested by a claim, or using the max_age parameter</p>
+ * 
+ * @pre <pre>ProfileRequestContext.getSubcontext(AuthenticationContext.class, false) != null</pre>
+ * @pre <pre>AuthenticationContext.getSubcontext(OpenIDConnectContext.class, false) != null</pre>
+ * @pre <pre>OpenIDConnectContext.getIDToken() != null</pre>
+ * @event {@link net.shibboleth.idp.authn.AuthnEventIds#NO_CREDENTIALS}
+ * @event {@link org.opensaml.profile.action.EventIds#PROCEED_EVENT_ID} 
+ * 
+ * @since 4.0.0
+ */
+//TODO P.S should some of these functions be delegated to Nimbus IDTokenValidator?
+public class ValidateIDTokenAuthenticationTime extends AbstractAuthenticationAction {
+
+    /** Class logger. */
+    @Nonnull private final Logger log = LoggerFactory.getLogger(ValidateIDTokenAuthenticationTime.class);
+
+    /**
+     * Clock skew - milliseconds before a lower time bound, or after an upper time bound, to consider still acceptable
+     * Default value: 3 minutes.
+     */
+    //TODO change these to durations or the JWT verifier
+     @NonNegative private long clockSkew;
+
+    /**
+     * Amount of time in milliseconds for which a forced authentication is valid after it is issued. Default value: 30
+     * seconds.
+     */
+    //TODO P.S is this not just messagelifetime.
+     @NonNegative private long authnLifetime;
+
+    /**
+     * Constructor.
+     */
+    public ValidateIDTokenAuthenticationTime() {
+        super();
+        setClockSkew(60 * 3 * 1000);
+        setAuthnLifetime(30 * 1000);
+    }
+
+    /**
+     * Get the clock skew.
+     * 
+     * @return the clock skew
+     */
+     @NonNegative
+    public long getClockSkew() {
+        return clockSkew;
+    }
+
+    /**
+     * Set the clock skew.
+     * 
+     * @param skew clock skew to set
+     */
+    public void setClockSkew( @NonNegative final long skew) {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+        clockSkew = Constraint.isGreaterThanOrEqual(0, skew, "Clock skew must be greater than or equal to 0");
+    }
+
+    /**
+     * Gets the amount of time, in milliseconds, for which a forced authentication is valid.
+     * 
+     * @return amount of time, in milliseconds, for which a forced authentication is valid
+     */
+     @NonNegative
+    public long getAuthnLifetime() {
+        return authnLifetime;
+    }
+
+    /**
+     * Sets the amount of time, in milliseconds, for which a forced authentication is valid.
+     * 
+     * @param lifetime amount of time, in milliseconds, for which a forced authentication is valid
+     */
+    public synchronized void setAuthnLifetime( @NonNegative final long lifetime) {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+        authnLifetime =
+                Constraint.isGreaterThanOrEqual(0, lifetime, "Authn lifetime must be greater than or equal to 0");
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext,
+            @Nonnull final AuthenticationContext authenticationContext) {
+        
+
+        if (!authenticationContext.isForceAuthn()) {
+           
+            return;
+        }
+        // If we have forced authentication, we will check for authentication age
+        final OpenIDConnectContext oidcCtx =
+                authenticationContext.getSubcontext(OpenIDConnectContext.class);
+        if (oidcCtx == null) {
+            log.error("{} Not able to find oidc context", getLogPrefix());
+            ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.NO_CREDENTIALS);
+            
+            return;
+        }
+        final Date authTimeDate;
+        try {
+            authTimeDate = oidcCtx.getIDToken().getJWTClaimsSet().getDateClaim("auth_time");
+        } catch (final ParseException e) {
+            log.error("{} Error parsing id token", getLogPrefix());
+            ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.NO_CREDENTIALS);            
+            return;
+        }
+        if (authTimeDate == null) {
+           // log.error("{} max age set but no auth_time received", getLogPrefix());
+            //ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.NO_CREDENTIALS);        
+            //FIXME WE ARE NOT CHECKING IF WE SET MAX_AGE HERE!!?
+            return;
+        }
+        final DateTime authTime = new DateTime(authTimeDate);
+        final DateTime now = new DateTime();
+        final DateTime latestValid = now.plus(getClockSkew());
+        final DateTime expiration = authTime.plus(getClockSkew() + getAuthnLifetime());
+
+        // Check authentication wasn't performed in the future
+        if (authTime.isAfter(latestValid)) {
+            log.warn("{} Authentication time is not yet valid: time was {}, latest valid is: {}", getLogPrefix(),
+                    authTime, latestValid);
+            ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.NO_CREDENTIALS);           
+            return;
+        }
+
+        // Check authentication time has not expired
+        if (expiration.isBefore(now)) {
+            log.warn("{} Authentication time has expired: time was '{}', expired at: '{}', current time: '{}'",
+                    getLogPrefix(), authTime, expiration, now);
+            ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.NO_CREDENTIALS);            
+            return;
+        }
+        
+    }
+
+}
\ No newline at end of file
diff --git a/java-idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/ValidateIDTokenAuthorizedParty.java b/java-idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/ValidateIDTokenAuthorizedParty.java
new file mode 100644
index 0000000..1f2cbb3
--- /dev/null
+++ b/java-idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/ValidateIDTokenAuthorizedParty.java
@@ -0,0 +1,89 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.idp.plugin.authn.oidc.rp.impl;
+
+import java.text.ParseException;
+
+import javax.annotation.Nonnull;
+
+import net.shibboleth.idp.authn.AbstractAuthenticationAction;
+import net.shibboleth.idp.authn.AuthnEventIds;
+import net.shibboleth.idp.authn.context.AuthenticationContext;
+import net.shibboleth.idp.plugin.authn.oidc.rp.context.OpenIDConnectContext;
+
+import org.opensaml.profile.action.ActionSupport;
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * An action that verifies the Authorized Party (azp) of an id_token. That is, the party to which
+ * the id_token was issued, or the presenter of the id_token. As the IdP will always be responsible
+ * for presenting the id_token, this value should always be the same client_id as the sole audience (aud).
+ * As such, azp is not strictly required, but is checked if present. 
+ * 
+ * <p>The authorized party is one or more case sensitive Strings or URIs.</p>
+ * 
+ * <p>See section 3.1.3.7 of the OpenID Connect core 1.0</p>
+ * 
+ * @pre <pre>ProfileRequestContext.getSubcontext(AuthenticationContext.class, false) != null</pre>
+ * @pre <pre>AuthenticationContext.getSubcontext(OpenIDConnectContext.class, false) != null</pre>
+ * @pre <pre>OpenIDConnectContext.getIDToken() != null</pre>
+ * @event {@link org.opensaml.profile.action.EventIds#PROCEED_EVENT_ID}
+ * @event {@link AuthnEventIds#NO_CREDENTIALS}
+ * 
+ * @since 4.0.0
+ */
+//Issues of semantics exist here e.g. https://bitbucket.org/openid/connect/issues/973/
+//TODO the code does not match the spec?
+public class ValidateIDTokenAuthorizedParty extends AbstractAuthenticationAction {
+
+    /** Class logger. */
+    @Nonnull private final Logger log = LoggerFactory.getLogger(ValidateIDTokenAuthorizedParty.class);
+
+    /** {@inheritDoc} */
+    @Override
+    protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext,
+            @Nonnull final AuthenticationContext authenticationContext) {
+        
+        final OpenIDConnectContext oidcCtx =
+                authenticationContext.getSubcontext(OpenIDConnectContext.class);
+        if (oidcCtx == null) {
+            log.error("{} Unable to find oidc context", getLogPrefix());
+            ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.NO_CREDENTIALS);            
+            return;
+        }
+
+        try {
+            if (oidcCtx.getIDToken().getJWTClaimsSet().getAudience().size() > 1) {
+                final String azp = oidcCtx.getIDToken().getJWTClaimsSet().getStringClaim("azp");
+                if (!oidcCtx.getClientID().getValue().equals(azp)) {
+                    log.error("{} multiple audiences, client is not the azp", getLogPrefix());
+                    ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.NO_CREDENTIALS);                   
+                    return;
+                }
+            }
+        } catch (final ParseException e) {
+            log.error("{} Error parsing id token", getLogPrefix());
+            ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.NO_CREDENTIALS);            
+            return;
+        }       
+        return;
+    }
+
+}
diff --git a/java-idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/ValidateIDTokenExpirationTime.java b/java-idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/ValidateIDTokenExpirationTime.java
new file mode 100644
index 0000000..b5c69f6
--- /dev/null
+++ b/java-idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/ValidateIDTokenExpirationTime.java
@@ -0,0 +1,87 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.idp.plugin.authn.oidc.rp.impl;
+
+import java.util.Date;
+
+import javax.annotation.Nonnull;
+
+import net.shibboleth.idp.authn.AbstractAuthenticationAction;
+import net.shibboleth.idp.authn.AuthnEventIds;
+import net.shibboleth.idp.authn.context.AuthenticationContext;
+import net.shibboleth.idp.plugin.authn.oidc.rp.context.OpenIDConnectContext;
+
+import org.opensaml.profile.action.ActionSupport;
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * An action that verifies the expiration time (exp) of an id_token. If the expiration time has past
+ * the id_token must not be accepted.
+ * 
+ * <p>Some clock skew could be specified, but is not in this implementation</p>
+ * 
+ * <p>Expiration time of the id_token is required. If not present, AuthnEventIds#NO_CREDENTIALS is returned</p>
+ * 
+ *
+ * @event {@link org.opensaml.profile.action.EventIds#PROCEED_EVENT_ID}
+ * @event {@link AuthnEventIds#NO_CREDENTIALS}
+ * @pre <pre>ProfileRequestContext.getSubcontext(AuthenticationContext.class, false) != null</pre>
+ * @pre <pre>AuthenticationContext.getSubcontext(OpenIDConnectContext.class, false) != null</pre>
+ * @pre <pre>OpenIdConnectContext.getOidcTokenResponse() != null</pre>
+ * 
+ * @since 4.0.0
+ */
+public class ValidateIDTokenExpirationTime extends AbstractAuthenticationAction {
+
+    /** Class logger. */
+    @Nonnull private final Logger log = LoggerFactory.getLogger(ValidateIDTokenExpirationTime.class);
+
+    /** {@inheritDoc} */
+    @Override
+    protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext,
+            @Nonnull final AuthenticationContext authenticationContext) {
+        
+
+        final OpenIDConnectContext oidcCtx =
+                authenticationContext.getSubcontext(OpenIDConnectContext.class);
+        if (oidcCtx == null) {
+            log.error("{} Unable to find oidc context", getLogPrefix());
+            ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.NO_CREDENTIALS);            
+            return;
+        }
+
+        // The current time MUST be before the time represented by the expiration time of the token
+        final Date currentDate = new Date();
+        try {
+            final Date expDate = oidcCtx.getIDToken().getJWTClaimsSet().getExpirationTime();
+            if (currentDate.after(expDate)) {
+                log.error("{} Current date {} is past exp date {}", getLogPrefix(), currentDate, expDate);
+                ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.NO_CREDENTIALS);                
+                return;
+            }
+        } catch (final java.text.ParseException | NullPointerException e) {
+            log.error("{} Error parsing id token", getLogPrefix(), e);
+            ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.NO_CREDENTIALS);            
+            return;
+        }        
+        return;
+    }
+
+}
diff --git a/java-idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/ValidateIDTokenIssuer.java b/java-idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/ValidateIDTokenIssuer.java
new file mode 100644
index 0000000..1f21a4b
--- /dev/null
+++ b/java-idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/ValidateIDTokenIssuer.java
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.idp.plugin.authn.oidc.rp.impl;
+
+import java.text.ParseException;
+
+import javax.annotation.Nonnull;
+
+import net.shibboleth.idp.authn.AbstractAuthenticationAction;
+import net.shibboleth.idp.authn.AuthnEventIds;
+import net.shibboleth.idp.authn.context.AuthenticationContext;
+import net.shibboleth.idp.plugin.authn.oidc.rp.context.OpenIDConnectContext;
+
+import org.opensaml.profile.action.ActionSupport;
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * An action that verifies the issuer (iss) of the id_token exactly matches that of the configured 
+ * OpenID Connect Provider.
+ * 
+ * @event {@link org.opensaml.profile.action.EventIds#PROCEED_EVENT_ID}
+ * @event {@link AuthnEventIds#NO_CREDENTIALS} 
+ * @pre <pre>ProfileRequestContext.getSubcontext(AuthenticationContext.class, false) != null</pre>
+ * @pre <pre>AuthenticationContext.getSubcontext(OpenIDConnectContext.class, false) != null</pre>
+ * @pre <pre>OpenIdConnectContext.getOidcTokenResponse() != null</pre>
+ * @pre <pre>OpenIdConnectContext.getoIDCProviderMetadata() != null</pre>
+ * 
+ * @since 4.0.0
+ */
+public class ValidateIDTokenIssuer extends AbstractAuthenticationAction {
+
+    /** Class logger. */
+    @Nonnull private final Logger log = LoggerFactory.getLogger(ValidateIDTokenIssuer.class);
+
+    /** {@inheritDoc} */
+    @Override
+    protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext,
+            @Nonnull final AuthenticationContext authenticationContext) {
+        
+
+        final OpenIDConnectContext oidcCtx =
+                authenticationContext.getSubcontext(OpenIDConnectContext.class);
+        if (oidcCtx == null) {
+            log.error("{} Unable to find oidc context", getLogPrefix());
+            ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.NO_CREDENTIALS);            
+            return;
+        }
+
+        final String issuer = oidcCtx.getoIDCProviderMetadata().getIssuer().getValue();
+
+        try {
+            if (!issuer.equals(oidcCtx.getIDToken().getJWTClaimsSet().getIssuer())) {
+                log.error("{} Issuer mismatch", getLogPrefix());
+                ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.NO_CREDENTIALS);               
+                return;
+            }
+
+        } catch (final ParseException e) {
+            log.error("{} Unable to parse oidc token", getLogPrefix());
+            ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.NO_CREDENTIALS);            
+            return;
+        }
+        
+        return;
+    }
+}
diff --git a/java-idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/ValidateIDTokenSignature.java b/java-idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/ValidateIDTokenSignature.java
new file mode 100644
index 0000000..8e1b26b
--- /dev/null
+++ b/java-idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/ValidateIDTokenSignature.java
@@ -0,0 +1,159 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.idp.plugin.authn.oidc.rp.impl;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.StringWriter;
+import java.security.interfaces.RSAPublicKey;
+import java.text.ParseException;
+import java.util.Map;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import net.shibboleth.idp.authn.AbstractAuthenticationAction;
+import net.shibboleth.idp.authn.AuthnEventIds;
+import net.shibboleth.idp.authn.context.AuthenticationContext;
+import net.shibboleth.idp.plugin.authn.oidc.rp.context.OpenIDConnectContext;
+
+import org.opensaml.profile.action.ActionSupport;
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Charsets;
+import com.google.common.io.CharStreams;
+import com.nimbusds.jose.JOSEException;
+import com.nimbusds.jose.crypto.RSASSAVerifier;
+import com.nimbusds.jose.jwk.RSAKey;
+import com.nimbusds.jose.shaded.json.JSONArray;
+import com.nimbusds.jose.shaded.json.JSONObject;
+import com.nimbusds.jose.util.JSONObjectUtils;
+import com.nimbusds.jwt.SignedJWT;
+
+/**
+ * An action that verifies the signature of a JWS id_token using the RSA key belonging to the keyID
+ * found in the JOSE Header.
+ * 
+ * <p>The current implementation *requires* the id_token is signed, and *requires* an RSA key type</p>
+ * 
+ * @event {@link org.opensaml.profile.action.EventIds#PROCEED_EVENT_ID}
+ * @event {@link AuthnEventIds#NO_CREDENTIALS}
+ * @pre <pre>ProfileRequestContext.getSubcontext(AuthenticationContext.class, false) != null</pre>
+ * @pre <pre>AuthenticationContext.getSubcontext(OpenIDConnectContext.class, false) != null</pre>
+ * @pre <pre>OpenIdConnectContext.getoIDCProviderMetadata() != null</pre>
+ * 
+ * @since 4.0.0
+ */
+public class ValidateIDTokenSignature extends AbstractAuthenticationAction {
+
+    /** Class logger. */
+    @Nonnull private final Logger log = LoggerFactory.getLogger(ValidateIDTokenSignature.class);
+
+    /** {@inheritDoc} */
+    @Override
+    protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext, 
+            @Nonnull final AuthenticationContext authenticationContext) {
+        
+        final OpenIDConnectContext oidcCtx =
+                authenticationContext.getSubcontext(OpenIDConnectContext.class);
+        if (oidcCtx == null) {
+            log.error("{} Unable to find oidc context", getLogPrefix());
+            ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.NO_CREDENTIALS);            
+            return;
+        }
+        SignedJWT signedJWT = null;
+        try {
+            //TODO P.S. oidcCtx.getIDToken() could be null.
+            signedJWT = SignedJWT.parse(oidcCtx.getIDToken().serialize());
+        } catch (final ParseException e) {
+            log.error("{} Error when parsing signed JWT", getLogPrefix(), e);
+            ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.NO_CREDENTIALS);            
+            return;
+        }
+        RSAPublicKey providerKey = null;
+        try {
+            final JSONObject key = getProviderRSAJWK(oidcCtx.getoIDCProviderMetadata().getJWKSetURI()
+                    .toURL().openStream(),signedJWT.getHeader().getKeyID());
+            if (key == null) {
+                log.error("{} Unable to find key to verify signature", getLogPrefix());
+                ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.NO_CREDENTIALS);                
+                return;
+            }
+            providerKey = RSAKey.parse(key).toRSAPublicKey();
+        } catch (final IOException | java.text.ParseException | JOSEException e) {
+            log.error("{} Error when parsing key to verify signature", getLogPrefix(), e);
+            ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.NO_CREDENTIALS);            
+            return;
+        }
+        final RSASSAVerifier verifier = new RSASSAVerifier(providerKey);
+        try {
+            if (!signedJWT.verify(verifier)) {
+                log.error("{} JWT signature verification failed", getLogPrefix());
+                ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.NO_CREDENTIALS);               
+                return;
+            }
+        } catch (final JOSEException e) {
+            log.error("{} JWT signature verification not performed", getLogPrefix(), e);
+            ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.NO_CREDENTIALS);            
+            return;
+        }
+        log.debug("{} ID Token signature verified",getLogPrefix());        
+        return;
+    }
+
+    /**
+     * Parse JWK and RSA public key from the input stream for signature verification.
+     * 
+     * @param is inputstream containing the JWK
+     * @param kid The key ID to be looked up
+     * @return RSA public key as a JSON Object. <code>Null</code> if there is no key
+     * @throws ParseException if parsing fails.
+     * @throws IOException if something unexpected happens.
+     */
+    //TODO could be cached? (or use Nimbus to do this validation as it does cache it). 
+    @Nullable
+    private JSONObject getProviderRSAJWK(@Nonnull final InputStream is, @Nullable final String kid) 
+            throws ParseException, IOException {
+        
+        if (kid == null) {
+            log.warn("No kid defined in the JWT, no signning key can be returned");
+        }
+
+        final StringWriter writer = new StringWriter();
+        CharStreams.copy(new InputStreamReader(is, Charsets.UTF_8), writer);
+
+        final Map<String,Object> json = JSONObjectUtils.parse(writer.toString());
+        final JSONArray keyList = (JSONArray) json.get("keys");
+        if (keyList == null) {            
+            return null;
+        }
+        for (final Object key : keyList) {
+            final JSONObject k = (JSONObject) key;
+            if ("sig".equals(k.get("use")) && "RSA".equals(k.get("kty"))) {
+                if (kid == null || kid.equals(k.get("kid"))) {                                   
+                    return k;
+                }
+            }
+        }        
+        return null;
+    }
+
+}
diff --git a/java-idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/ValidateOIDCAuthentication.java b/java-idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/ValidateOIDCAuthentication.java
new file mode 100644
index 0000000..d0eac70
--- /dev/null
+++ b/java-idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/ValidateOIDCAuthentication.java
@@ -0,0 +1,209 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.idp.plugin.authn.oidc.rp.impl;
+
+import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import javax.security.auth.Subject;
+
+import net.shibboleth.idp.attribute.IdPAttribute;
+import net.shibboleth.idp.attribute.StringAttributeValue;
+import net.shibboleth.idp.authn.AbstractValidationAction;
+import net.shibboleth.idp.authn.AuthenticationResult;
+import net.shibboleth.idp.authn.AuthnEventIds;
+import net.shibboleth.idp.authn.context.AuthenticationContext;
+import net.shibboleth.idp.plugin.authn.oidc.rp.context.OpenIDConnectContext;
+import net.shibboleth.idp.authn.principal.IdPAttributePrincipal;
+import net.shibboleth.idp.authn.principal.UsernamePrincipal;
+import net.shibboleth.utilities.java.support.annotation.constraint.Live;
+import net.shibboleth.utilities.java.support.component.ComponentSupport;
+import net.shibboleth.utilities.java.support.primitive.StringSupport;
+
+import org.opensaml.profile.action.ActionSupport;
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.nimbusds.jwt.JWTClaimsSet;
+import com.nimbusds.openid.connect.sdk.claims.UserInfo;
+
+/**
+ * An action that builds an {@link AuthenticationResult} from the subject (sub claim) of the OpenID Connect token.
+ * 
+ * <p>Also builds any OpenID Connect Standard claims as {@link IdPAttributePrincipal}s, and sets them
+ *  alongside the subject principal for use during attribute resolution.</p>
+ * 
+ * 
+ * @event {@link org.opensaml.profile.action.EventIds#PROCEED_EVENT_ID}
+ * @event {@link AuthnEventIds#NO_CREDENTIALS}
+ * @pre <pre>AuthenticationContext.getSubcontext(OpenIDConnectContext.class, false) != null</pre>
+ * @post If AuthenticationContext.getSubcontext(OpenIDConnectContext.class, false).getIDToken()
+ * .getJWTClaimsSet().getSubject()!= null, then an {@link net.shibboleth.idp.authn.AuthenticationResult} 
+ * is saved to the {@link AuthenticationContext}.
+ * 
+ * @since 4.0.0
+ */
+ at SuppressWarnings({"rawtypes", "unchecked"})
+public class ValidateOIDCAuthentication extends AbstractValidationAction {
+
+    /** Class logger. */
+    @Nonnull private final Logger log = LoggerFactory.getLogger(ValidateOIDCAuthentication.class);
+
+    /** Avoid creating multiple principals. */
+    private boolean avoidMultiplePrincipal;
+
+    /** the subject received from id token. */
+    @Nullable private String oidcSubject;
+
+    /** The JWT Claim Set of the ID Token acquired from the token endpoint. */
+    @Nullable private JWTClaimsSet jwtClaims;
+
+    /**
+     * In MFA use case prior authentication may have created a usernameprincipal already with value not matching to MFA.
+     * 
+     * @param avoid true if additional principals should be avoided.
+     */
+    public void setAvoidMultiplePrincipal(final boolean avoid) {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+        avoidMultiplePrincipal = avoid;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected boolean doPreExecute(@Nonnull final ProfileRequestContext profileRequestContext, 
+            @Nonnull final AuthenticationContext authenticationContext) {
+
+        if (!super.doPreExecute(profileRequestContext, authenticationContext)) {
+            return false;
+        }
+        log.trace("{}: Prerequisities fulfilled to start doPreExecute", getLogPrefix());
+
+        final OpenIDConnectContext oidcCtx = authenticationContext.getSubcontext(OpenIDConnectContext.class);
+        if (oidcCtx == null) {
+            log.error("{} Unable to find oidc context", getLogPrefix());
+            ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.NO_CREDENTIALS);            
+            return false;
+        }
+
+        if (oidcCtx.getIDToken() == null) {
+            log.error("{} No ID Token in response", getLogPrefix());
+            ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.NO_CREDENTIALS);            
+            return false;
+        }
+        try {
+            //TODO P.S. this will fail if not decrypted JWE. 
+            oidcSubject = StringSupport.trimOrNull(oidcCtx.getIDToken().getJWTClaimsSet().getSubject());
+            jwtClaims = oidcCtx.getIDToken().getJWTClaimsSet();
+        } catch (final ParseException e) {
+            log.error("{} unable to parse ID Token", getLogPrefix());
+            ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.NO_CREDENTIALS);            
+            return false;
+        }
+
+        if (oidcSubject == null) {
+            log.error("{} Subject is null in ID Token response", getLogPrefix());
+            ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.NO_CREDENTIALS);            
+            return false;
+        }
+        return true;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext, 
+            @Nonnull final AuthenticationContext authenticationContext) {
+        
+        buildAuthenticationResult(profileRequestContext, authenticationContext);
+        return;
+    }
+
+    /**
+     * For each OIDC Standard Claim (OpenID Connect Core 1.0 section 5.1) in the JWT Claim set {@code jwtClaimSet},
+     * build a {@link IdPAttributePrincipal} using a {@link StringAttributeValue}.
+     * <p>
+     * Any claim that is not understood (not in the standard claim set) *will* be ignored.
+     * <p>
+     * All claims are strings except {@code email_verified} and {code phone_verified) (both booleans), {@code address}
+     * (JSON Object), and {@code updated_at} (number). Both booleans are represented as string attribute values, the
+     * {@code address} and {@code updated_at} claims are currently ignored.
+     * 
+     * 
+     * @return a list of standard OIDC claims as {@link IdPAttributePrincipal}s.
+     */
+    @Nonnull 
+    @Live
+    private List<IdPAttributePrincipal> buildIdPAttributePrincipalsFromStandardClaims() {
+
+        final List<IdPAttributePrincipal> claimPrincipals = new ArrayList<>();
+        if (jwtClaims != null) {
+            // jwtClaims.getClaims() is never null
+            for (final Map.Entry<String, Object> claim : jwtClaims.getClaims().entrySet()) {
+
+                if (UserInfo.getStandardClaimNames().contains(claim.getKey())) {
+
+                    String claimValue = null;
+                    if (claim.getValue() instanceof String) {
+                        claimValue = StringSupport.trimOrNull((String) claim.getValue());
+                    }
+
+                    if (claim.getValue() instanceof Boolean) {
+                        claimValue = StringSupport.trimOrNull(Boolean.toString((Boolean) claim.getValue()));
+                    }
+                    if (claimValue == null) {
+                        log.trace("{} JWT Claim [{}] is not of a supported type or is null/empty, ignored",
+                                getLogPrefix(), claim);
+                        continue;
+                    }
+
+                    final IdPAttribute idpAttr = new IdPAttribute(claim.getKey());
+                    idpAttr.setValues(Collections.singletonList(new StringAttributeValue(claimValue)));
+
+                    final IdPAttributePrincipal attrPrincipal = new IdPAttributePrincipal(idpAttr);
+                    log.trace("{} Constructed IdPAttributePrincipal from OIDC claim [{}]", getLogPrefix(),
+                            attrPrincipal);
+                    claimPrincipals.add(attrPrincipal);
+                }
+
+            }
+        }
+
+        return claimPrincipals;
+
+    }
+
+    @Override
+    protected Subject populateSubject(@Nonnull final Subject subject) {    
+        
+        if (avoidMultiplePrincipal && subject.getPrincipals().size() > 0) {
+            log.debug("{} Subject already contains principal, not populated", getLogPrefix());
+
+        } else {            
+            subject.getPrincipals().add(new UsernamePrincipal(oidcSubject));
+            subject.getPrincipals().addAll(buildIdPAttributePrincipalsFromStandardClaims());
+
+        }        
+        return subject;
+    }
+
+}
diff --git a/java-idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/ValidateOIDCAuthenticationResponse.java b/java-idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/ValidateOIDCAuthenticationResponse.java
new file mode 100644
index 0000000..01c9603
--- /dev/null
+++ b/java-idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/ValidateOIDCAuthenticationResponse.java
@@ -0,0 +1,123 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.idp.plugin.authn.oidc.rp.impl;
+
+import javax.annotation.Nonnull;
+
+import net.shibboleth.idp.authn.AbstractExtractionAction;
+import net.shibboleth.idp.authn.AuthnEventIds;
+import net.shibboleth.idp.authn.context.AuthenticationContext;
+import net.shibboleth.idp.plugin.authn.oidc.rp.context.OpenIDConnectContext;
+import net.shibboleth.utilities.java.support.primitive.StringSupport;
+
+import org.opensaml.profile.action.ActionSupport;
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.nimbusds.oauth2.sdk.ParseException;
+import com.nimbusds.oauth2.sdk.id.State;
+import com.nimbusds.openid.connect.sdk.AuthenticationErrorResponse;
+import com.nimbusds.openid.connect.sdk.AuthenticationResponse;
+import com.nimbusds.openid.connect.sdk.AuthenticationResponseParser;
+import com.nimbusds.openid.connect.sdk.AuthenticationSuccessResponse;
+
+/**
+ * An action that extracts the id_token from the query parameters of a successful response from
+ * an OpenID Connect provider. The id_token is attached to the {@link OpenIDConnectContext}.  
+ *  
+ * <p>Also checks the state returned by the provider matches that which was sent to the provider</p>
+ * 
+ * @pre <pre>ProfileRequestContext.getSubcontext(AuthenticationContext.class, false) != null</pre>
+ * @pre <pre>AuthenticationContext.getSubcontext(OpenIDConnectContext.class, false) != null</pre>
+ * @pre <pre>OpenIdConnectContext.getAuthenticationResponseURI() != null</pre>
+ * @post If {@link AuthenticationResponse#indicatesSuccess()} == true, the extracted id_token
+ * is extracted and placed into the existing {@link OpenIDConnectContext}.
+ * @event {@link org.opensaml.profile.action.EventIds#PROCEED_EVENT_ID}
+ * @event {@link AuthnEventIds#NO_CREDENTIALS}
+ * 
+ * @since 4.0.0
+ */
+public class ValidateOIDCAuthenticationResponse extends AbstractExtractionAction {
+
+    /*
+     * A DRAFT PROTO CLASS!! NOT TO BE USED YET.
+     * 
+     * FINAL GOAL IS TO MOVE FROM CURRENT OIDC TO MORE WEBFLOW LIKE IMPLEMENTATION.
+     */
+
+    /** Class logger. */
+    @Nonnull private final Logger log = LoggerFactory.getLogger(ValidateOIDCAuthenticationResponse.class);
+
+    /** {@inheritDoc} */
+    @Override
+    protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext,
+            @Nonnull final AuthenticationContext authenticationContext) {
+        
+
+        final OpenIDConnectContext oidcCtx =
+                authenticationContext.getSubcontext(OpenIDConnectContext.class);
+        if (oidcCtx == null) {
+            log.info("{} Unable to find oidc context", getLogPrefix());
+            ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.NO_CREDENTIALS);            
+            return;
+        }
+
+        if (oidcCtx.getAuthenticationResponseURI() == null) {
+            log.info("{} Response URI not set", getLogPrefix());
+            ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.NO_CREDENTIALS);            
+            return;
+        }
+        log.debug("Validating OIDC response {}", oidcCtx.getAuthenticationResponseURI().toString());
+        AuthenticationResponse response = null;
+        try {
+            response = AuthenticationResponseParser.parse(oidcCtx.getAuthenticationResponseURI());
+        } catch (final ParseException e) {
+            log.info("{} Response parsing failed", getLogPrefix(),e);
+            ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.NO_CREDENTIALS);            
+            return;
+        }
+        if (!response.indicatesSuccess()) {
+            
+            final AuthenticationErrorResponse errorResponse = (AuthenticationErrorResponse) response;
+            String error = errorResponse.getErrorObject().getCode();
+            final String errorDescription = errorResponse.getErrorObject().getDescription();
+            if (StringSupport.trimOrNull(errorDescription) != null) {
+                error += " : " + errorDescription;
+            }
+           
+            log.info("{} response indicated error: {}", getLogPrefix(), error);
+            ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.NO_CREDENTIALS);            
+            return;
+        }
+        final AuthenticationSuccessResponse successResponse = (AuthenticationSuccessResponse) response;
+        // implicit and hybrid flows return id token in response.
+        oidcCtx.setIDToken(successResponse.getIDToken());
+        final State state = oidcCtx.getState();
+        if (state == null || !state.equals(successResponse.getState())) {
+            log.info("{} OIDC flow state mismatch:", getLogPrefix());
+            ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.NO_CREDENTIALS);
+            
+        }
+
+        oidcCtx.setAuthenticationSuccessResponse(successResponse);
+        
+        return;
+    }
+
+}
diff --git a/java-idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/package-info.java b/java-idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/package-info.java
new file mode 100644
index 0000000..9cacce6
--- /dev/null
+++ b/java-idp-oidc-rp-impl/src/main/java/net/shibboleth/idp/plugin/authn/oidc/rp/impl/package-info.java
@@ -0,0 +1,22 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * This package has an authentication implementation for OpenID Connect authentication providers.
+ */
+
+package net.shibboleth.idp.plugin.authn.oidc.rp.impl;
\ No newline at end of file
diff --git a/java-idp-oidc-rp-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml b/java-idp-oidc-rp-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml
new file mode 100644
index 0000000..6bf6aee
--- /dev/null
+++ b/java-idp-oidc-rp-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans"
+    xmlns:context="http://www.springframework.org/schema/context"
+    xmlns:util="http://www.springframework.org/schema/util" xmlns:p="http://www.springframework.org/schema/p"
+    xmlns:c="http://www.springframework.org/schema/c" 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/util http://www.springframework.org/schema/util/spring-util.xsd"
+
+    default-init-method="initialize" default-destroy-method="destroy">
+
+    <!-- 
+    System beans needed for extension to function, loaded after global.xml.
+    The default template shows an incomplete example authentication flow descriptor which can be
+    removed if not needed 
+    -->
+  
+    <bean id="authn/OIDCRelyingParty" parent="shibboleth.AuthenticationFlow"
+            p:order="%{idp.authn.OIDC.RP.order:1000}"
+            p:nonBrowserSupported="%{idp.authn.OIDC.RP.nonBrowserSupported:true}"
+            p:passiveAuthenticationSupported="%{idp.authn.OIDC.RP.passiveAuthenticationSupported:true}"
+            p:forcedAuthenticationSupported="%{idp.authn.OIDC.RP.forcedAuthenticationSupported:true}"
+            p:proxyRestrictionsEnforced="%{idp.authn.OIDC.RP.proxyRestrictionsEnforced:%{idp.authn.enforceProxyRestrictions:true}}"
+            p:proxyScopingEnforced="%{idp.authn.OIDC.RP.proxyScopingEnforced:false}"
+            p:discoveryRequired="%{idp.authn.OIDC.RP.discoveryRequired:false}"
+            p:lifetime="%{idp.authn.OIDC.RP.lifetime:%{idp.authn.defaultLifetime:PT1H}}"
+            p:inactivityTimeout="%{idp.authn.OIDC.RP.inactivityTimeout:%{idp.authn.defaultTimeout:PT30M}}"
+            p:reuseCondition-ref="#{'%{idp.authn.OIDC.RP.reuseCondition:shibboleth.Conditions.TRUE}'.trim()}"
+            p:activationCondition-ref="#{'%{idp.authn.OIDC.RP.activationCondition:shibboleth.Conditions.TRUE}'.trim()}">
+        <property name="supportedPrincipals">
+            <list>
+                <bean parent="shibboleth.SAML2AuthnContextClassRef"
+                    c:classRef="class-ref" />
+                <bean parent="shibboleth.SAML1AuthenticationMethod"
+                    c:method="auth-ref" />
+            </list>
+        </property>
+        <property name="supportedPrincipalsByString">
+            <bean parent="shibboleth.CommaDelimStringArray"
+                c:_0="#{'%{idp.authn.OIDC.RP.supportedPrincipals:}'.trim()}" />
+        </property>
+    </bean>
+    
+    <!-- Controller implementation -->
+    <bean id="shibboleth.OpenIDConnectStartServlet"
+        class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.AuthorizationController" />
+  
+</beans>
\ No newline at end of file
diff --git a/java-idp-oidc-rp-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/OIDCRelyingParty/oidc-relying-party-beans.xml b/java-idp-oidc-rp-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/OIDCRelyingParty/oidc-relying-party-beans.xml
new file mode 100644
index 0000000..cb9040b
--- /dev/null
+++ b/java-idp-oidc-rp-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/OIDCRelyingParty/oidc-relying-party-beans.xml
@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:context="http://www.springframework.org/schema/context"
+       xmlns:util="http://www.springframework.org/schema/util"
+       xmlns:p="http://www.springframework.org/schema/p"
+       xmlns:c="http://www.springframework.org/schema/c"
+       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/util http://www.springframework.org/schema/util/spring-util.xsd"
+                           
+       default-init-method="initialize"
+       default-destroy-method="destroy">
+       
+    <bean class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer"
+        p:placeholderPrefix="%{" p:placeholderSuffix="}" />
+
+    <bean class="net.shibboleth.ext.spring.config.IdentifiableBeanPostProcessor" />
+    <bean class="net.shibboleth.idp.profile.impl.ProfileActionBeanPostProcessor" />
+    
+    <!-- Spring controller to start the authentication request and recieve the response -->
+    <bean id="shibboleth.authn.OpenIDConnect.externalAuthnPath" class="java.lang.String"
+        c:_0="servletRelative:/Authn/OIDC/RP/auth">
+    </bean>
+    
+    <!-- TODO Add user import here
+    <import resource="openidconnect-authn-config.xml" /> -->
+
+    <bean id="SetRPUIInformation" class="net.shibboleth.idp.ui.impl.SetRPUIInformation"
+        scope="prototype" p:activationCondition-ref="shibboleth.authn.oidc.rp.populateUIInfo"
+        p:httpServletRequest-ref="shibboleth.HttpServletRequest">
+        <property name="fallbackLanguages">
+            <bean parent="shibboleth.CommaDelimStringArray" c:_0="%{idp.ui.fallbackLanguages:}" />
+        </property>
+    </bean>
+    
+    <!-- Populate RP UI info from metadata? -->
+    <util:constant id="shibboleth.authn.oidc.rp.populateUIInfo" static-field="java.lang.Boolean.FALSE" />
+
+    <bean id="SetOIDCInformation" class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.SetOIDCInformation"
+        p:redirectURI="%{idp.oidc.rp.redirectURI:}"
+        p:clientID="%{idp.oidc.rp.clientID:null}"
+        p:clientSecret="%{idp.oidc.rp.clientSecret:null}"
+        p:providerMetadataLocation="%{idp.oidc.rp.providerConfigurationDocument:null}"
+        p:scope="%{idp.oidc.rp.scope:null}">
+            
+        <!-- <property name="responseType" value="id_token token"/> -->
+    </bean>
+
+    <bean id="ValidateOIDCAuthenticationResponse"
+        class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.ValidateOIDCAuthenticationResponse"
+        p:httpServletRequest-ref="shibboleth.HttpServletRequest" />
+
+    <bean id="ValidateIDTokenACR" class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.ValidateIDTokenACR" />
+
+    <bean id="ValidateIDTokenAudience" class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.ValidateIDTokenAudience" />
+
+    <bean id="ValidateIDTokenAuthenticationTime"
+        class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.ValidateIDTokenAuthenticationTime" />
+
+    <bean id="ValidateIDTokenAuthorizedParty"
+        class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.ValidateIDTokenAuthorizedParty" />
+
+    <bean id="ValidateIDTokenExpirationTime"
+        class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.ValidateIDTokenExpirationTime" />
+
+    <bean id="ValidateIDTokenIssuer" class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.ValidateIDTokenIssuer" />
+
+    <bean id="ValidateIDTokenSignature"
+        class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.ValidateIDTokenSignature" />
+
+    <bean id="ValidateOIDCAuthentication" class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.ValidateOIDCAuthentication" scope="prototype" />
+
+    <bean id="GetOIDCTokenResponse" class="net.shibboleth.idp.plugin.authn.oidc.rp.impl.GetOIDCTokenResponse"
+        p:httpServletRequest-ref="shibboleth.HttpServletRequest" />
+    
+
+    
+</beans>
diff --git a/java-idp-oidc-rp-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/OIDCRelyingParty/oidc-relying-party-flow.xml b/java-idp-oidc-rp-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/OIDCRelyingParty/oidc-relying-party-flow.xml
new file mode 100644
index 0000000..eaa7e9b
--- /dev/null
+++ b/java-idp-oidc-rp-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/authn/OIDCRelyingParty/oidc-relying-party-flow.xml
@@ -0,0 +1,60 @@
+<flow xmlns="http://www.springframework.org/schema/webflow" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://www.springframework.org/schema/webflow http://www.springframework.org/schema/webflow/spring-webflow.xsd"
+	parent="authn.abstract, authn/conditions">
+
+	<action-state id="SetRPUIInformation">
+        <evaluate expression="SetRPUIInformation" />
+        <evaluate expression="'proceed'" />
+        <transition on="proceed" to="SetOIDCInformation" /> 
+    </action-state>
+
+    <action-state id="SetOIDCInformation">
+        <evaluate expression="SetOIDCInformation" />
+        <evaluate expression="'proceed'" />
+        <transition on="proceed" to="ExternalTransfer" />
+    </action-state>
+
+    <view-state id="ExternalTransfer"
+        view="externalRedirect:#{T(net.shibboleth.idp.authn.ExternalAuthentication).getExternalRedirect(flowRequestContext.getActiveFlow().getApplicationContext().getBean('shibboleth.authn.OpenIDConnect.externalAuthnPath'), flowExecutionContext.getKey().toString())}">
+        <attribute name="csrf_excluded" value="true" type="boolean" />       
+        <on-render>
+            <evaluate
+                expression="opensamlProfileRequestContext.getSubcontext(T(net.shibboleth.idp.authn.context.AuthenticationContext)).addSubcontext(new net.shibboleth.idp.authn.context.ExternalAuthenticationContext(new net.shibboleth.idp.authn.impl.ExternalAuthenticationImpl(false)), true).setFlowExecutionUrl(flowExecutionUrl + '&_eventId_proceed=1')" />
+          </on-render>
+        <transition to="ValidateResponse" />
+    </view-state>
+
+    <action-state id="ValidateResponse">
+        <evaluate expression="ValidateOIDCAuthenticationResponse" />
+        <evaluate expression="'proceed'" />
+        <transition on="proceed" to="GetToken" />
+    </action-state>
+
+    <action-state id="GetToken">
+        <evaluate expression="GetOIDCTokenResponse" />
+        <evaluate expression="'proceed'" />
+        <transition on="proceed" to="ValidateOIDCTokenResponse" />
+    </action-state>
+
+    <action-state id="ValidateOIDCTokenResponse">
+        <evaluate expression="ValidateIDTokenACR" />
+        <evaluate expression="ValidateIDTokenAudience" />
+        <evaluate expression="ValidateIDTokenAuthorizedParty" />
+        <evaluate expression="ValidateIDTokenExpirationTime" />
+        <evaluate expression="ValidateIDTokenIssuer" />
+        <evaluate expression="ValidateIDTokenSignature" />
+        <evaluate expression="ValidateIDTokenAuthenticationTime" />
+        <evaluate expression="'proceed'" />
+        <transition on="proceed" to="SetPrincipal" />
+    </action-state>
+
+
+    <action-state id="SetPrincipal">
+        <evaluate expression="ValidateOIDCAuthentication" />
+        <evaluate expression="'proceed'" />
+        <transition on="proceed" to="proceed" />
+    </action-state>
+
+    <bean-import resource="oidc-relying-party-beans.xml" />
+
+</flow>
diff --git a/java-idp-oidc-rp-impl/src/main/resources/META-INF/services/net.shibboleth.idp.module.IdPModule b/java-idp-oidc-rp-impl/src/main/resources/META-INF/services/net.shibboleth.idp.module.IdPModule
new file mode 100644
index 0000000..5e849fd
--- /dev/null
+++ b/java-idp-oidc-rp-impl/src/main/resources/META-INF/services/net.shibboleth.idp.module.IdPModule
@@ -0,0 +1 @@
+net.shibboleth.idp.plugin.authn.oidc.rp.OIDCRPModule
diff --git a/java-idp-oidc-rp-impl/src/main/resources/META-INF/services/net.shibboleth.idp.plugin.IdPPlugin b/java-idp-oidc-rp-impl/src/main/resources/META-INF/services/net.shibboleth.idp.plugin.IdPPlugin
new file mode 100644
index 0000000..bae01fe
--- /dev/null
+++ b/java-idp-oidc-rp-impl/src/main/resources/META-INF/services/net.shibboleth.idp.plugin.IdPPlugin
@@ -0,0 +1,2 @@
+net.shibboleth.idp.plugin.authn.oidc.rp.OIDCRPPlugin
+  
\ No newline at end of file
diff --git a/java-idp-oidc-rp-impl/src/main/resources/net/shibboleth/idp/plugin/authn/oidc/rp/conf/authn/oidc-rp.properties b/java-idp-oidc-rp-impl/src/main/resources/net/shibboleth/idp/plugin/authn/oidc/rp/conf/authn/oidc-rp.properties
new file mode 100644
index 0000000..7e1b3ce
--- /dev/null
+++ b/java-idp-oidc-rp-impl/src/main/resources/net/shibboleth/idp/plugin/authn/oidc/rp/conf/authn/oidc-rp.properties
@@ -0,0 +1,7 @@
+idp.oidc.rp.clientID=client_id
+idp.oidc.rp.clientSecret=secret
+##does not need the .well-known/openid-configuration path.
+idp.oidc.rp.providerConfigurationDocument=https://hostname
+idp.oidc.rp.redirectURI=https://localhost:8443/idp/profile/Authn/OIDC/RP/callback
+## openid is defaulted. Other scopes could be; profile etc.
+#idp.oidc.rp.scope=openid
\ No newline at end of file
diff --git a/java-idp-oidc-rp-impl/src/main/resources/net/shibboleth/idp/plugin/authn/oidc/rp/module.properties b/java-idp-oidc-rp-impl/src/main/resources/net/shibboleth/idp/plugin/authn/oidc/rp/module.properties
new file mode 100644
index 0000000..1926077
--- /dev/null
+++ b/java-idp-oidc-rp-impl/src/main/resources/net/shibboleth/idp/plugin/authn/oidc/rp/module.properties
@@ -0,0 +1,14 @@
+# Example Properties defining an authentication module.
+
+# Class to Module ID mappings
+net.shibboleth.idp.plugin.authn.oidc.rp.OIDCRPModule = idp.authn.java-idp-oidc-rp
+
+# Module Owner
+idp.authn.java-idp-oidc-rp.plugin = net.shibboleth.idp.plugin.authn.java-idp-oidc-rp
+
+idp.authn.java-idp-oidc-rp.name = java-idp-oidc-rp Authentication
+idp.authn.java-idp-oidc-rp.desc = Login flow for java-idp-oidc-rp
+idp.authn.java-idp-oidc-rp.url = /java-idp-oidc-rpConfiguration
+idp.authn.java-idp-oidc-rp.1.src = somefile
+idp.authn.java-idp-oidc-rp.1.dest = conf/to-somefile
+idp.authn.java-idp-oidc-rp.1.replace = true
diff --git a/java-idp-oidc-rp-impl/src/main/resources/net/shibboleth/idp/plugin/authn/oidc/rp/plugin.properties b/java-idp-oidc-rp-impl/src/main/resources/net/shibboleth/idp/plugin/authn/oidc/rp/plugin.properties
new file mode 100644
index 0000000..c4407d2
--- /dev/null
+++ b/java-idp-oidc-rp-impl/src/main/resources/net/shibboleth/idp/plugin/authn/oidc/rp/plugin.properties
@@ -0,0 +1,9 @@
+# Example properties defining this plugin
+
+plugin.id = net.shibboleth.idp.plugin.authn.oidc.rp.OIDCRPPlugin
+# Only used when package manifest is not available
+plugin.version = 0.0.1
+#plugin.license = licence.txt
+
+# No prereqs
+#plugin.modules.required =
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..a889736
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,145 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+	<parent>
+		<groupId>net.shibboleth</groupId>
+		<artifactId>parent</artifactId>
+		<version>11.2.0</version>
+	</parent>
+	<groupId>net.shibboleth.idp.plugin.authn</groupId>
+	<artifactId>idp-plugin-oidc-rp-parent</artifactId>
+	<version>0.0.1-SNAPSHOT</version>
+	<name>Shibboleth IdP :: Plugins :: OIDC RP Login Flow</name>
+	<packaging>pom</packaging>
+	<description>Java OIDC RP authentication plugin for the Shibboleth IdP.</description>
+	
+	<properties>
+		<idp.groupId>net.shibboleth.idp</idp.groupId>
+		<idp.version>4.1.0</idp.version>
+		<opensaml.groupId>org.opensaml</opensaml.groupId>
+		<opensaml.version>4.1.0</opensaml.version>
+		<oidc.common.version>1.1.0-SNAPSHOT</oidc.common.version>
+		<checkstyle.configLocation>${project.basedir}/checkstyle.xml</checkstyle.configLocation>
+	</properties>
+	
+	<distributionManagement>
+		<site>
+			<id>site</id>
+			<url>dav:...</url>
+		</site>
+	</distributionManagement>
+	
+	<repositories>
+		<repository>
+			<id>shib-release</id>
+			<url>https://build.shibboleth.net/nexus/content/groups/public</url>
+			<snapshots>
+				<enabled>false</enabled>
+			</snapshots>
+		</repository>
+		<repository>
+			<id>shib-snapshot</id>
+			<url>https://build.shibboleth.net/nexus/content/repositories/snapshots</url>
+			<releases>
+				<enabled>false</enabled>
+			</releases>
+		</repository>
+	</repositories>
+
+	<modules>
+		<module>java-idp-oidc-rp-impl</module>
+		<module>java-idp-oidc-rp-api</module>
+		<!-- All DIST modules must come after the -api and -impl modules -->
+		<module>java-idp-oidc-rp-dist</module>
+	</modules>
+
+	<scm>
+		<connection>scm:git:...</connection>
+		<developerConnection>scm:git:...</developerConnection>
+		<url>...</url>
+	</scm>
+	
+	<dependencies>
+		<!-- Project wide test Dependencies -->
+		<dependency>
+			<groupId>org.testng</groupId>
+			<artifactId>testng</artifactId>
+			<scope>test</scope>
+		</dependency>
+		<dependency>
+			<groupId>${slf4j.groupId}</groupId>
+			<artifactId>slf4j-api</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.mockito</groupId>
+			<artifactId>mockito-core</artifactId>
+			<scope>test</scope>
+		</dependency>
+	</dependencies>
+	<dependencyManagement>
+		<!-- java-idp-oidc-rp project dependencies -->
+		<dependencies>
+			<dependency>
+				<groupId>net.shibboleth.idp.plugin.authn</groupId>
+				<artifactId>idp-plugin-oidc-rp-api</artifactId>
+				<version>${project.version}</version>
+			</dependency>
+			<dependency>
+				<groupId>net.shibboleth.idp.plugin.authn</groupId>
+				<artifactId>idp-plugin-oidc-rp-impl</artifactId>
+				<version>${project.version}</version>
+			</dependency>
+			<!-- OIDC Common BOM when importing OIDC dependencies -->
+             <dependency>
+                <groupId>net.shibboleth.oidc</groupId>
+                <artifactId>oidc-common-bom</artifactId>
+                <version>${oidc.common.version}</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+			<!-- Shibboleth IdP BOM for importing IdP dependencies -->
+			<dependency>
+				<groupId>net.shibboleth.idp</groupId>
+				<artifactId>idp-bom</artifactId>
+				<version>${idp.version}</version>
+				<type>pom</type>
+				<scope>import</scope>
+			</dependency>
+			<!-- Provided shibboleth support dependencies -->
+			<dependency>
+				<groupId>net.shibboleth.utilities</groupId>
+				<artifactId>java-support</artifactId>
+				<version>${java-support.version}</version>
+				<scope>provided</scope>
+			</dependency>
+			<!-- Test bom dependencies -->
+			<dependency>
+				<groupId>${idp.groupId}</groupId>
+				<artifactId>idp-tests-bom</artifactId>
+				<version>${idp.version}</version>
+				<type>pom</type>
+				<scope>import</scope>
+			</dependency>
+		</dependencies>
+	</dependencyManagement>
+	<build>
+		<plugins>
+			<plugin>
+				<groupId>org.apache.maven.plugins</groupId>
+				<artifactId>maven-jar-plugin</artifactId>
+				<configuration>
+					<archive>
+						<index>true</index>
+						<manifestEntries>
+							<Automatic-Module-Name>${automatic.module.name}</Automatic-Module-Name>
+							<Sealed>true</Sealed>
+						</manifestEntries>
+					</archive>
+				</configuration>
+			</plugin>
+		</plugins>
+	</build>
+
+</project>

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.


More information about the commits mailing list