Skip to content

Commit

Permalink
Merge pull request #38 from sweid4keycloak/38-keycloak-21.x-support
Browse files Browse the repository at this point in the history
 Support for Keycloak 24.x
  • Loading branch information
ullgren authored Apr 23, 2024
2 parents 45e4724 + e4de8aa commit 0116fde
Show file tree
Hide file tree
Showing 8 changed files with 117 additions and 62 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ target/
.project
.settings/
.classpath
dependency-reduced-pom.xml
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
FROM quay.io/keycloak/keycloak:20.0.5
FROM quay.io/keycloak/keycloak:24.0.3
COPY target/bankid4keycloak-*.jar /opt/keycloak/providers
72 changes: 40 additions & 32 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@
BankID4Keycloak is an identity provider for Keycloak, giving it superpowers by adding support for Swedish BankID.
Please note, in order to use this in production a valid BankID contract is required, for more information see [this page](https://www.bankid.com/utvecklare/guider).

:warning: Since `Keycloak v21.0.0` this extension cannot be used effectively. The old AngularJS-based Admin-UI got removed completely and at least for now there's no way of displaying all necessary configuration options in order to configure this extension properly in Keycloak. To fix this there needs to be changes made to code Keycloak see [keycloak #15344](https://github.com/keycloak/keycloak/issues/15344).
For now, the latest version this extension is compatible with is `v20.0.5` provided that you switch the admin theme to `keycloak`.

## Legal Notice

Expand Down Expand Up @@ -33,57 +31,67 @@ In order to access the BankID API a client certificate and a truststore is requi


### Client certificate
To use BankID in production a valid contract is required, please contact one of the banks acting as resellers for [more information](https://www.bankid.com/utvecklare/guider/skapa-fp-certifikat).
To use BankID in production a valid contract is required, please contact one of the banks acting as resellers for [more information](https://www.bankid.com/en/utvecklare/guider/skapa-fp-certifikat).

A certificate for the BankID test environment can be downloaded using the following [page](https://www.bankid.com/utvecklare/test)
A certificate for the BankID test environment can be downloaded using the following [page](https://www.bankid.com/en/utvecklare/test)

The password for the PKCS12 container and the private key is: qwerty123

### Truststore
The CA Certificate is available in the PDF "BankID Relying Party Guidelines v3.2.22" and can also found on this [page](https://www.bankid.com/bankid-i-dina-tjanster/rp-info). See pages 13 and 14 of the PDF for production and test certificates.
The CA Certificate is available in the official [Integration guide, API](https://www.bankid.com/en/utvecklare/guider/teknisk-integrationsguide) under the [Environments](https://www.bankid.com/en/utvecklare/guider/teknisk-integrationsguide/miljoer) section.

*example of how to create a PKCS12 truststore from a pem formated file*
`keytool -importcert -file apa.pem -alias "BankID Test CA" -trustcacerts -storetype pkcs12 -keystore truststore.p12`

*example of how to create a PKCS12 truststore from a pem formated file*

`keytool -importcert -file Test_BankID_SSL_Root_CA_v1_Test.pem -alias "BankID Test CA" -trustcacerts -storetype pkcs12 -keystore truststore.p12`


## Configure

Start Keycloak and log in to the admin console.

:warning: It's not yet compatible to the new `Admin UI (keycloak.v2)` from Keycloak. If you want to use this provider, you need to enable
the old Admin UI for the respective realm (mostly `master`, see this
paper [Keycloak 19.0.0 release](https://www.keycloak.org/2022/07/keycloak-1900-released.html#_new_admin_console_is_now_the_default_console))
Under the "Identity Providers" heading add a "BankID e-legitimation" identity provider.

### Client ID
This field is currently added as a required field to all identity providers by the core Keycloak theme. The BankID e-legitimation identity provider does not use this field so fill in any value.

Under the "Identity Providers" heading add the "BankID e-legitimation" identity provider.
### Client Secret
This field is currently added as a required field to all identity providers by the core Keycloak theme. The BankID e-legitimation identity provider does not use this field so fill in any value.

**BankID API base URL:**
The URL for the BankID api. Please refer to the [BankID Relying Party Guidelines](https://www.bankid.com/bankid-i-dina-tjanster/rp-info) in case the URL has changed.
### Display order
Number defining the order of the providers in GUI (for example, on the Login page). The lowest number will be applied first.

### BankID API base URL
The URL for the BankID api. Please refer to the [BankID Relying Party Guidelines](https://www.bankid.com/bankid-i-dina-tjanster/rp-info) in case the URL has changed. +
At the time of writing they are
- Prod --> https://appapi2.bankid.com
- Test --> https://appapi2.test.bankid.com

**Keystore file:**
Full path to the keystore file.
*example*
`/tls/keystore.p12`
### Keystore file
Full path to the keystore file.

**Keystore password:**
Password for the PKCS12 container.
*example*
`qwerty123`
*example*: `/tls/keystore.p12`

### Keystore password

Password for the PKCS12 container.

*example*: `qwerty123`

**Password for the private key:**
The private key inside the PKCS12 container is also encrypted.
*example*
`qwerty123`
### Password for the private key

**Truststore file:**
Full path to the truststore file.
*example*
`/tls/truststore.p12`
The private key inside the PKCS12 container is also encrypted.

*example*: `qwerty123`

### Truststore file

Full path to the truststore file.

*example*: `/tls/truststore.p12`

### Truststore password

**Truststore password:**
Password for the PKCS12 container.
*example*
`qwerty123`

*example*: `qwerty123`
4 changes: 2 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@
<modelVersion>4.0.0</modelVersion>
<groupId>sweid4keycloak</groupId>
<artifactId>bankid4keycloak</artifactId>
<version>20.1.0-SNAPSHOT</version>
<version>24.0.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>11</java.version>
<maven.compiler.source>${java.version}</maven.compiler.source>
<maven.compiler.target>${java.version}</maven.compiler.target>
<keycloak.version>20.0.5</keycloak.version>
<keycloak.version>24.0.3</keycloak.version>
<google.zxing.version>3.4.0</google.zxing.version>
</properties>
<licenses>
Expand Down
29 changes: 13 additions & 16 deletions src/main/java/org/keycloak/broker/bankid/BankidEndpoint.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,17 @@

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.ws.rs.FormParam;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.ResponseBuilder;
import javax.ws.rs.core.Response.Status;
import javax.ws.rs.core.CacheControl;
import jakarta.ws.rs.FormParam;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.QueryParam;
import jakarta.ws.rs.core.Context;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.core.Response.ResponseBuilder;
import jakarta.ws.rs.core.Response.Status;
import jakarta.ws.rs.core.CacheControl;

import org.infinispan.Cache;
import org.jboss.logging.Logger;
Expand Down Expand Up @@ -61,9 +61,6 @@ public class BankidEndpoint {

private static String qrCodePrefix = "bankid.";

@Context
protected KeycloakSession session;

public BankidEndpoint(BankidIdentityProvider provider, BankidIdentityProviderConfig config,
AuthenticationCallback callback) {
this.config = config;
Expand Down Expand Up @@ -111,7 +108,7 @@ private Response doLogin(String nin, String state) {

try {
AuthResponse authResponse;
authResponse = bankidClient.sendAuth(nin, session.getContext().getConnection().getRemoteAddr());
authResponse = bankidClient.sendAuth(nin, provider.getSession().getContext().getConnection().getRemoteAddr());

UUID bankidRef = UUID.randomUUID();
this.actionTokenCache.put(bankidRef.toString(), authResponse, MAX_CACHE_LIFESPAN, TimeUnit.MINUTES);
Expand Down Expand Up @@ -183,7 +180,7 @@ public Response done(@QueryParam("state") String state, @QueryParam("bankidref")
// Make sure to remove the authresponse attribute from the session
try {
AuthenticationSessionModel authSession = this.callback.getAndVerifyAuthenticationSession(state);
session.getContext().setAuthenticationSession(authSession);
provider.getSession().getContext().setAuthenticationSession(authSession);
BrokeredIdentityContext identity = new BrokeredIdentityContext(
getConfig().getAlias().concat("." + getUsername(user)));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import java.net.URI;
import java.net.URISyntaxException;

import javax.ws.rs.core.Response;
import jakarta.ws.rs.core.Response;

import org.apache.http.client.HttpClient;
import org.keycloak.broker.provider.AbstractIdentityProvider;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,15 @@ public class BankidIdentityProviderConfig extends IdentityProviderModel {
*/
private static final long serialVersionUID = 3849007589404817838L;

private static final String BANKID_APIURL_PROPERTY_NAME = "bankid_apiurl";
private static final String BANKID_KEYSTORE_FILE_PROPERTY_NAME = "bankid_keystore_file";
private static final String BANKID_KEYSTORE_PASSWORD_PROPERTY_NAME = "bankid_keystore_password";
private static final String BANKID_TRUSTSTORE_FILE_PROPERTY_NAME = "bankid_truststore_file";
private static final String BANKID_TRUSTSTORE_PASSWORD_PROPERTY_NAME = "bankid_truststore_password";
private static final String BANKID_PRIVATEKEY_PASSWORD_PROPERTY_NAME = "bankid_privatekey_password";
private static final String BANKID_REQUIRE_NIN = "bankid_require_nin";
private static final String BANKID_SHOW_QR_CODE = "bankid_show_qr_code";
private static final String BANKID_SAVE_NIN_HASH = "bankid_save_nin_hash";
public static final String BANKID_APIURL_PROPERTY_NAME = "bankid_apiurl";
public static final String BANKID_KEYSTORE_FILE_PROPERTY_NAME = "bankid_keystore_file";
public static final String BANKID_KEYSTORE_PASSWORD_PROPERTY_NAME = "bankid_keystore_password";
public static final String BANKID_TRUSTSTORE_FILE_PROPERTY_NAME = "bankid_truststore_file";
public static final String BANKID_TRUSTSTORE_PASSWORD_PROPERTY_NAME = "bankid_truststore_password";
public static final String BANKID_PRIVATEKEY_PASSWORD_PROPERTY_NAME = "bankid_privatekey_password";
public static final String BANKID_REQUIRE_NIN = "bankid_require_nin";
public static final String BANKID_SHOW_QR_CODE = "bankid_show_qr_code";
public static final String BANKID_SAVE_NIN_HASH = "bankid_save_nin_hash";

private KeyStore keyStore;
private KeyStore truststore;
Expand All @@ -36,6 +36,10 @@ public String getApiUrl() {
return getConfig().get(BANKID_APIURL_PROPERTY_NAME);
}

public void setApiUrl(final String apiUrl) {
getConfig().put(BANKID_APIURL_PROPERTY_NAME, apiUrl);
}

public KeyStore getKeyStore() throws Exception {
if (keyStore == null) {
keyStore = KeystoreUtil.loadKeyStore(
Expand Down Expand Up @@ -66,6 +70,11 @@ public boolean isRequiredNin() {
return Boolean.valueOf(getConfig().getOrDefault(BANKID_REQUIRE_NIN, "false"));
}

public void setRequiredNin(boolean requiredNin) {
getConfig().put(BANKID_REQUIRE_NIN, String.valueOf(requiredNin));
}


public boolean isSaveNinHashed() {
return Boolean.valueOf(getConfig().getOrDefault(BANKID_SAVE_NIN_HASH, "false"));
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
package org.keycloak.broker.bankid;

import java.util.List;

import org.keycloak.broker.provider.AbstractIdentityProviderFactory;
import org.keycloak.models.IdentityProviderModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.provider.ProviderConfigProperty;
import org.keycloak.provider.ProviderConfigurationBuilder;

public class BankidIdentityProviderFactory extends AbstractIdentityProviderFactory<BankidIdentityProvider> {
public class BankidIdentityProviderFactory extends AbstractIdentityProviderFactory <BankidIdentityProvider> {

public static final String PROVIDER_ID = "bankid";

Expand All @@ -28,4 +32,40 @@ public String getId() {
return PROVIDER_ID;
}

@Override
public List<ProviderConfigProperty> getConfigProperties() {
return ProviderConfigurationBuilder.create()
.property().name(BankidIdentityProviderConfig.BANKID_APIURL_PROPERTY_NAME).label("BankID API base URL").helpText("The base URL for the BankID API. Without the trailing slash. Normally https://appapi2.bankid.com")
.type(ProviderConfigProperty.STRING_TYPE).add()

.property().name(BankidIdentityProviderConfig.BANKID_KEYSTORE_FILE_PROPERTY_NAME).label("Keystore file").helpText("Full path to the keystore file, that holds the client certificate, including filename i.e. /path/to/file/myfile.p12")
.type(ProviderConfigProperty.STRING_TYPE).add()

.property().name(BankidIdentityProviderConfig.BANKID_KEYSTORE_PASSWORD_PROPERTY_NAME).label("Keystore password").helpText("The password for the keystore.")
.type(ProviderConfigProperty.PASSWORD).add()

.property().name(BankidIdentityProviderConfig.BANKID_PRIVATEKEY_PASSWORD_PROPERTY_NAME).label("Password for the private key").helpText("Password for the private key.")
.type(ProviderConfigProperty.PASSWORD).add()

.property().name(BankidIdentityProviderConfig.BANKID_TRUSTSTORE_FILE_PROPERTY_NAME).label("Truststore file").helpText("Full path to the truststore file, that holds the CA for the server certificate, including filename i.e. /path/to/file/bankid-truststore.p12")
.type(ProviderConfigProperty.STRING_TYPE).add()

.property().name(BankidIdentityProviderConfig.BANKID_TRUSTSTORE_PASSWORD_PROPERTY_NAME).label("Truststore password").helpText("The password for the truststore.")
.type(ProviderConfigProperty.PASSWORD).add()

.property().name(BankidIdentityProviderConfig.BANKID_REQUIRE_NIN).label("Require Personal Number").helpText("Require the user to provide their Personal Number before logging in.")
.defaultValue(false)
.type(ProviderConfigProperty.BOOLEAN_TYPE).add()

.property().name(BankidIdentityProviderConfig.BANKID_SHOW_QR_CODE).label("Show QR code").helpText("Show QR code to allow user to use when starting the authentication.")
.defaultValue(true)
.type(ProviderConfigProperty.BOOLEAN_TYPE).add()

.property().name(BankidIdentityProviderConfig.BANKID_SAVE_NIN_HASH).label("Use hashed Personal Number").helpText("Used hashed (SHA-256) Personal Number in keycloak instead of storing it in clear text.")
.defaultValue(false)
.type(ProviderConfigProperty.BOOLEAN_TYPE).add()

.build();
}

}

0 comments on commit 0116fde

Please sign in to comment.