Skip to content
This repository was archived by the owner on Feb 17, 2022. It is now read-only.

Commit b4a17c0

Browse files
Merge pull request #2 from curityio/signicat-java-lib
Implementation using the Signicat Java Connector
2 parents a2d4802 + d322687 commit b4a17c0

File tree

11 files changed

+387
-142
lines changed

11 files changed

+387
-142
lines changed

README.md

Lines changed: 0 additions & 5 deletions
This file was deleted.

README.rst

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
Signicat Authenticator Plug-in
2+
==============================
3+
4+
.. image:: https://travis-ci.org/curityio/signicat-authenticator.svg?branch=master
5+
:target: https://travis-ci.org/curityio/signicat-authenticator
6+
7+
An authenticator that uses the Signicat signing service to do authentication
8+
This project provides an opens source Signicat Authenticator plug-in for the Curity Identity Server. This allows an administrator to add functionality to Curity which will then enable end users to login using their Signicat credentials -- or more exactly -- the credentials of some E-ID provider, like BankID or NemID. The app that integrates with Curity will be provided with all of the attributes released by the user at Signicat, including the user's personal number and other biographical information.
9+
10+
System Requirements
11+
~~~~~~~~~~~~~~~~~~~
12+
13+
Curity Identity Server 2.4.0 and `its system requirements <https://developer.curity.io/docs/latest/system-admin-guide/system-requirements.html>`_
14+
15+
Requirements for Building from Source
16+
"""""""""""""""""""""""""""""""""""""
17+
18+
The source code is written entirely in `Kotlin <http://kotlinlang.org/>`_. It can be compiled using Maven 3. For this to succeed, however, the `Signicat Connector for Java <https://support.signicat.com/display/S2/Signicat+Connector+for+Java>`_ needs to be installed into a Maven repository which is accessible during compilation. The `POM <pom.xml>`_ may need to be updated depending on the Maven Coordinates (Group, Artifact, Version) used during installation. Refer to the `Maven guide for information about installing third-party JARs <https://maven.apache.org/guides/mini/guide-3rd-party-jars-local.html>`_. Once the Signicat Connector's JAR and its associated OpenSAML version are installed, the project can be compiled from a shell by issuing a command like this: ``mvn package``.
19+
20+
Installation
21+
~~~~~~~~~~~~
22+
23+
To install this plug-in, either download a binary version available from the `releases section of this project's GitHub repository <https://github.com/curityio/signicat-authenticator/releases>`_ or compile it from source (as described above). If you compiled the plug-in from source, the package will be placed in the ``target`` subdirectory. The resulting JAR file or the one downloaded from GitHub needs to placed in the directory ``${IDSVR_HOME}/usr/share/plugins/signicat``. (The name of the last directory, ``signicat``, which is the plug-in group, is arbitrary and can be anything.) All of the dependent JAR files must be placed in this directory as well. These include:
24+
25+
* signicat-client-lib-4.0.1.jar
26+
* signicat-opensaml-1.1-PATCH-6.jar
27+
* commons-codec-1.10.jar
28+
* xmlsec-1.5.8.jar
29+
30+
All of these JAR files can be obtained by downloading the `Signicat Connector for Java <https://support.signicat.com/display/S2/Signicat+Connector+for+Java>`_. Apache Commons Codec and Apache Santuario can be downloaded from Maven central or their respective project web sites.
31+
32+
.. note::
33+
34+
The Signicat Connector ZIP file contains other JAR files as well (e.g., SLF4J, Apache Commons Logging, etc.). These are not required by this plug-in, but installing them should not adversely effect the plug-in either.
35+
36+
Once the plug-in and its dependencies are placed into the plug-in group directory, it will become available as soon as each node is restarted.
37+
38+
For a more detailed explanation of installing plug-ins, refer to the `Curity developer guide <https://developer.curity.io/docs/latest/developer-guide/plugins/index.html#plugin-installation>`_.
39+
40+
Installing from Source
41+
""""""""""""""""""""""
42+
43+
During development of the plug-in, it is very easy to copy the plug-in JAR and its dependencies with the following one-liner:
44+
45+
.. code:: bash
46+
47+
mvn install dependency:copy-dependencies \
48+
-DincludeScope=runtime \
49+
-DoutputDirectory=$IDSVR_HOME/lib/plugins/signicat && \
50+
cp target/identityserver.plugins.authenticators.signicat-*.jar $IDSVR_HOME/lib/plugins/signicat
51+
52+
Because the server must be restarted after this, it can be quite tedious and time consuming. For that reason, it is better to use `Intellij's HotSwap capability <https://www.jetbrains.com/help/idea/reloading-classes.html>`_ to reload the classes after compilation. This will allow a developer to HotSwap changes without requiring a restart. If it fails to HotSwap some change, however, the above technique can be used.
53+
54+
Creating a Signicat Authenticator in Curity
55+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
56+
57+
The easiest way to configure a new Signicat authenticator is using the Curity admin UI. The configuration for this can be downloaded as XML or CLI commands later, so only the steps to do this in the GUI will be described.
58+
59+
1. Go to the ``Authenticators`` page of the authentication profile wherein the authenticator instance should be created.
60+
2. Click the ``New Authenticator`` button.
61+
3. Enter a name (e.g., ``signicat1``). For production, this name needs to match the URI component in the callback URL whitelisted by Signicat.
62+
4. For the type, pick the ``Signicat`` option.
63+
5. On the next page, you can define all of the standard authenticator configuration options like any previous authenticator that should run, the resulting ACR, transformers that should executed, etc. At the bottom of the configuration page, the Signicat-specific options can be found.
64+
65+
.. figure:: docs/images/signicat-authenticator-type-in-curity.png
66+
:align: center
67+
:width: 600px
68+
69+
Using these inputs, certain required and optional configuration settings may be provided.
70+
71+
.. note::
72+
73+
The Signicat-specific configuration is generated dynamically based on the `configuration model defined in the Kotlin interface <https://github.com/curityio/signicat-authenticator/blob/master/src/main/kotlin/io/curity/identityserver/plugin/signicat/config/SignicatAuthenticatorPluginConfig.kt>`_.
74+
75+
6. From the ``Country`` dropdown box, pick the country's kind of E-ID that should be used. For example, pick ``sweden`` to use Swedish BankID or ``denmark`` to use NemID.
76+
7. Enter the ``Service Name`` that you have registered with Signicat or use the default of ``demo`` for testing.
77+
8. From the ``Environment`` dropdown box, select either ``standard-environment`` or ``custom-environment``. The former should be used if you are not using a custom domain (e.g., ``signicat.example.com``). If not, then select ``standard-environment`` and pick either ``production`` or ``pre-production``. ``pre-production`` will cause certain test certificates to be used and warnings to be logged in the server log.
78+
9. Optionally, enter the name of a `graphics profile <https://support.signicat.com/display/S2/Graphical+profiles%2C+fonts+and+styling>`_ in the ``Graphics Profile`` text field.
79+
80+
Once all of these changes are made, they will be staged, but not committed (i.e., not running). To make them active, click the ``Commit`` menu option in the ``Changes`` menu. Optionally enter a comment in the ``Deploy Changes`` dialogue and click ``OK``.
81+
82+
Once the configuration is committed and running, the authenticator can be used like any other.
83+
84+
.. note::
85+
86+
When using the authenticator with the Curity Security Token Service (i.e., the "OAuth server"), if the client application sends the OpenID-Connect-defined ``ui_locales`` request parameter, that will be passed to Signicat as the preferred language. Also, if a request has been made by some other client (in the same browser) using the ``ui_locales``, this preferred language will be propagated to Signicat even if the application does not explicitly provide it in the request.
87+
88+
License
89+
~~~~~~~
90+
91+
This plugin and its associated documentation is listed under the `Apache 2 license <LICENSE>`_.
92+
93+
More Information
94+
~~~~~~~~~~~~~~~~
95+
96+
Please visit `curity.io <https://curity.io/>`_ for more information about the Curity Identity Server.
97+
98+
Copyright (C) 2018 Curity AB.
Loading

pom.xml

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131

3232
<groupId>io.curity.identityserver.plugin</groupId>
3333
<artifactId>identityserver.plugins.authenticators.signicat</artifactId>
34-
<version>1.0.0</version>
34+
<version>0.9-SNAPSHOT</version>
3535
<packaging>jar</packaging>
3636
<name>Signicat Authenticator</name>
3737

@@ -92,16 +92,19 @@
9292
<groupId>se.curity.identityserver</groupId>
9393
<artifactId>identityserver.sdk</artifactId>
9494
<version>2.4.0</version>
95+
<scope>provided</scope>
9596
</dependency>
9697
<dependency>
9798
<groupId>org.slf4j</groupId>
9899
<artifactId>slf4j-api</artifactId>
99100
<version>1.7.22</version>
101+
<scope>provided</scope>
100102
</dependency>
101103
<dependency>
102104
<groupId>org.hibernate</groupId>
103105
<artifactId>hibernate-validator</artifactId>
104106
<version>5.1.3.Final</version>
107+
<scope>provided</scope>
105108
</dependency>
106109
<dependency>
107110
<groupId>org.jetbrains.kotlin</groupId>
@@ -110,10 +113,24 @@
110113
<scope>provided</scope>
111114
</dependency>
112115
<dependency>
113-
<groupId>org.jetbrains.kotlin</groupId>
114-
<artifactId>kotlin-test</artifactId>
115-
<version>${kotlin.version}</version>
116-
<scope>test</scope>
116+
<groupId>com.signicat</groupId>
117+
<artifactId>java-connector</artifactId>
118+
<version>4.0.1</version>
119+
</dependency>
120+
<dependency>
121+
<groupId>com.signicat</groupId>
122+
<artifactId>opensaml</artifactId>
123+
<version>1.1-PATCH-6</version>
124+
</dependency>
125+
<dependency>
126+
<groupId>commons-codec</groupId>
127+
<artifactId>commons-codec</artifactId>
128+
<version>1.10</version>
129+
</dependency>
130+
<dependency>
131+
<groupId>org.apache.santuario</groupId>
132+
<artifactId>xmlsec</artifactId>
133+
<version>1.5.8</version>
117134
</dependency>
118135
</dependencies>
119136
</project>

src/main/kotlin/io/curity/identityserver/plugin/signicat/authentication/SignicatAuthenticatorRequestHandler.kt

Lines changed: 77 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -16,51 +16,102 @@
1616

1717
package io.curity.identityserver.plugin.signicat.authentication
1818

19+
import io.curity.identityserver.plugin.signicat.config.Country
20+
import io.curity.identityserver.plugin.signicat.config.PredefinedEnvironment
1921
import io.curity.identityserver.plugin.signicat.config.SignicatAuthenticatorPluginConfig
22+
import io.curity.identityserver.plugin.signicat.descriptor.SignicatAuthenticatorPluginDescriptor
23+
import org.slf4j.Logger
24+
import org.slf4j.LoggerFactory
2025
import se.curity.identityserver.sdk.authentication.AuthenticationResult
2126
import se.curity.identityserver.sdk.authentication.AuthenticatorRequestHandler
22-
import se.curity.identityserver.sdk.http.HttpStatus
27+
import se.curity.identityserver.sdk.errors.ErrorCode
28+
import se.curity.identityserver.sdk.http.RedirectStatusCode
2329
import se.curity.identityserver.sdk.web.Request
2430
import se.curity.identityserver.sdk.web.Response
25-
import se.curity.identityserver.sdk.web.Response.ResponseModelScope.NOT_FAILURE
26-
import se.curity.identityserver.sdk.web.ResponseModel.templateResponseModel
31+
import java.net.URL
32+
import java.util.IllformedLocaleException
33+
import java.util.Locale
2734
import java.util.Optional
28-
import java.util.Collections.emptyMap
29-
import java.util.Collections.singletonMap
3035

3136
class RequestModel(request: Request)
3237

3338
class SignicatAuthenticatorRequestHandler(config : SignicatAuthenticatorPluginConfig)
3439
: AuthenticatorRequestHandler<RequestModel>
3540
{
36-
val userPreferenceManager = config.userPreferenceManager
41+
private val logger: Logger = LoggerFactory.getLogger(SignicatAuthenticatorRequestHandler::class.java)
42+
private val exceptionFactory = config.exceptionFactory
43+
private val environment = config.environment
44+
private val service = config.serviceName
45+
private val profile = config.graphicsProfile
46+
private val country = config.country
47+
private val authenticationInformationProvider = config.authenticationInformationProvider
48+
private val preferredLanguage = config.userPreferencesManager.locales
3749

38-
private object ViewDataKeys
39-
{
40-
internal val USERNAME = "_username"
41-
}
42-
43-
override fun preProcess(request: Request, response: Response): RequestModel
44-
{
45-
// set the template and model for responses on the NOT_FAILURE scope
46-
response.setResponseModel(templateResponseModel(
47-
singletonMap<String, Any>(ViewDataKeys.USERNAME, userPreferenceManager.username),
48-
"authenticate/get"), NOT_FAILURE)
49-
50-
// on request validation failure, we should use the same template as for NOT_FAILURE
51-
response.setResponseModel(templateResponseModel(emptyMap(), "authenticate/get"), HttpStatus.BAD_REQUEST)
52-
53-
return RequestModel(request)
54-
}
50+
override fun preProcess(request: Request, response: Response): RequestModel = RequestModel(request)
5551

5652
override fun get(requestModel: RequestModel, response: Response): Optional<AuthenticationResult>
5753
{
58-
return Optional.empty()
54+
return handle(requestModel, response)
5955
}
6056
override fun post(requestModel: RequestModel, response: Response): Optional<AuthenticationResult>
6157
{
62-
// TODO: Start signing process or return
58+
// Strange but fine if the client wants to do a post to start the flow
59+
60+
return handle(requestModel, response)
61+
}
62+
63+
private fun handle(requestModel: RequestModel, response: Response): Optional<AuthenticationResult>
64+
{
65+
val authUrl = authenticationInformationProvider.fullyQualifiedAuthenticationUri
66+
val target = URL(authUrl.toURL(), "${authUrl.path}/${SignicatAuthenticatorPluginDescriptor.CALLBACK}")
67+
68+
logger.debug("Redirecting to Signicat with the callback URL of {}", target)
69+
70+
val method = when (country) {
71+
Country.SWEDEN -> "sbid"
72+
Country.DENMARK -> "nemid"
73+
Country.ESTONIA -> "esteid"
74+
Country.FINLAND -> "tupas"
75+
Country.NORWAY -> "nbid"
76+
}
77+
val env = environment.customEnvironment.orElseGet {
78+
environment.standardEnvironment.map { it ->
79+
when (it)
80+
{
81+
PredefinedEnvironment.PRE_PRODUCTION -> "preprod"
82+
PredefinedEnvironment.PRODUCTION -> "id"
83+
// This and the other exceptional case below are guaranteed by the data model to never happen, but
84+
// this fact isn't know in the type system. So, these cases are handled to avoid bogus warnings,
85+
// but they will not occur.
86+
null -> throw exceptionFactory.internalServerException(ErrorCode.CONFIGURATION_ERROR)
87+
}
88+
}.orElseThrow { throw exceptionFactory.internalServerException(ErrorCode.CONFIGURATION_ERROR) }
89+
}
90+
var id = "$method:"
91+
92+
profile.ifPresent { id += it }
93+
94+
if (preferredLanguage != null)
95+
{
96+
val bcp47languageTag = preferredLanguage.split(' ', limit = 1)[0] // Use only first
97+
98+
try
99+
{
100+
val lang = Locale.forLanguageTag(bcp47languageTag).language.toLowerCase()
101+
102+
id += ":$lang"
103+
}
104+
catch (_ : IllformedLocaleException)
105+
{
106+
logger.debug("The prefered language '$preferredLanguage' could not be parsed, so it will not be " +
107+
"sent to Signicat")
108+
}
109+
}
110+
111+
val location = "https://$env.signicat.com/std/method/$service?id=$id&target=$target"
63112

64-
return Optional.empty()
113+
// Use a 303 in case this a POST request, so that the user agent is guaranteed (by compliance with HTTP) to
114+
// strip the body posted here before following the redirect.
115+
throw exceptionFactory.redirectException(location, RedirectStatusCode.SEE_OTHER)
65116
}
66117
}

0 commit comments

Comments
 (0)