Merge pull request #350 from n0emis/saml-client-cert
|
@ -198,18 +198,19 @@ these are rarely used for various reasons.
|
|||
|
||||
| config file | environment | example value | description |
|
||||
| ----------- | --------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| `saml` | | `{idpSsoUrl: ..., idpCert: ..., issuer: ..., identifierFormat: ..., disableRequestedAuthnContext: ..., groupAttribute: ..., externalGroups: [], requiredGroups: [], attribute: {id: ..., username: ..., email: ...}}` | An object detailing your SAML provider. Refer to the [OneLogin](guides/auth/saml-onelogin.md) and [SAML](guides/auth/saml.md) guides for more details! |
|
||||
| | `CMD_SAML_IDPSSOURL` | `https://idp.example.com/sso` | authentication endpoint of IdP. for details, see [guide](guides/auth/saml-onelogin.md). |
|
||||
| | `CMD_SAML_IDPCERT` | `/path/to/cert.pem` | certificate file path of IdP in PEM format |
|
||||
| | `CMD_SAML_ISSUER` | no example | Issuer to supply to identity provider (optional, default: `serverURL` config)" |
|
||||
| | `CMD_SAML_DISABLEREQUESTEDAUTHNCONTEXT` | `true` or `false` | true to allow any authentication method, false restricts to password authentication (PasswordProtectedTransport) method (default: false) |
|
||||
| | `CMD_SAML_IDENTIFIERFORMAT` | no example | name identifier format (optional, default: `urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress`) |
|
||||
| | `CMD_SAML_GROUPATTRIBUTE` | `memberOf` | attribute name for group list (optional) |
|
||||
| | `CMD_SAML_REQUIREDGROUPS` | `codimd-users` | group names that allowed (use vertical bar to separate) (optional) |
|
||||
| | `CMD_SAML_EXTERNALGROUPS` | `Temporary-staff` | group names that not allowed (use vertical bar to separate) (optional) |
|
||||
| | `CMD_SAML_ATTRIBUTE_ID` | `sAMAccountName` | attribute map for `id` (optional, default: NameID of SAML response) |
|
||||
| | `CMD_SAML_ATTRIBUTE_USERNAME` | `mailNickname` | attribute map for `username` (optional, default: NameID of SAML response) |
|
||||
| | `CMD_SAML_ATTRIBUTE_EMAIL` | `mail` | attribute map for `email` (optional, default: NameID of SAML response if `CMD_SAML_IDENTIFIERFORMAT` is default) |
|
||||
| `saml` | | `{idpSsoUrl: ..., idpCert: ..., clientCert: ..., issuer: ..., identifierFormat: ..., disableRequestedAuthnContext: ..., groupAttribute: ..., externalGroups: [], requiredGroups: [], attribute: {id: ..., username: ..., email: ...}}` | An object detailing your SAML provider. Refer to the [OneLogin](guides/auth/saml-onelogin.md) and [SAML](guides/auth/saml.md) guides for more details! |
|
||||
| | `CMD_SAML_IDPSSOURL` | `https://idp.example.com/sso` | authentication endpoint of IdP. for details, see [guide](guides/auth/saml-onelogin.md). |
|
||||
| | `CMD_SAML_IDPCERT` | `/path/to/cert.pem` | certificate file path of IdP in PEM format |
|
||||
| | `CMD_SAML_CLIENTCERT` | `/path/to/privatecert.pem` | certificate file path for the client in PEM format (optional) |
|
||||
| | `CMD_SAML_ISSUER` | no example | Issuer to supply to identity provider (optional, default: `serverURL` config)" |
|
||||
| | `CMD_SAML_DISABLEREQUESTEDAUTHNCONTEXT` | `true` or `false` | true to allow any authentication method, false restricts to password authentication (PasswordProtectedTransport) method (default: false) |
|
||||
| | `CMD_SAML_IDENTIFIERFORMAT` | no example | name identifier format (optional, default: `urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress`) |
|
||||
| | `CMD_SAML_GROUPATTRIBUTE` | `memberOf` | attribute name for group list (optional) |
|
||||
| | `CMD_SAML_REQUIREDGROUPS` | `codimd-users` | group names that allowed (use vertical bar to separate) (optional) |
|
||||
| | `CMD_SAML_EXTERNALGROUPS` | `Temporary-staff` | group names that not allowed (use vertical bar to separate) (optional) |
|
||||
| | `CMD_SAML_ATTRIBUTE_ID` | `sAMAccountName` | attribute map for `id` (optional, default: NameID of SAML response) |
|
||||
| | `CMD_SAML_ATTRIBUTE_USERNAME` | `mailNickname` | attribute map for `username` (optional, default: NameID of SAML response) |
|
||||
| | `CMD_SAML_ATTRIBUTE_EMAIL` | `mail` | attribute map for `email` (optional, default: NameID of SAML response if `CMD_SAML_IDENTIFIERFORMAT` is default) |
|
||||
|
||||
### Twitter Login
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# Keycloak/Red Hat SSO (self-hosted)
|
||||
OAuth with Keycloak/Red Hat SSO (self-hosted)
|
||||
|
||||
## Prerequisites
|
||||
|
||||
|
|
113
docs/guides/auth/saml-keycloak.md
Normal file
|
@ -0,0 +1,113 @@
|
|||
# How to setup CodiMD SAML with Keycloak
|
||||
## Configuring Keycloak
|
||||
### Get the public certificate
|
||||
1. Select the Realm you want to use for your CodiMD SAML
|
||||
2. Select "Realm Settings" in left sidebar
|
||||
3. Select the "Keys" tab
|
||||
4. Click the button "Certificate" at `RS256` algorithm
|
||||

|
||||
5. Copy this key and save it to the file specified in `saml.idpCert` property of the CodiMD configuration or `CMD_SAML_IDPCERT` environment variable
|
||||
|
||||
### Create a new client
|
||||
1. Select "Client" in left sidebar
|
||||

|
||||
2. Click on the "Create" button
|
||||
3. Set a Client ID and specify this in `saml.issuer` property of the CodiMD configuration or `CMD_SAML_ISSUER` environment variable
|
||||
4. Select `SAML` as Client Protocol
|
||||
5. Set Client SAML Endpoint to `https://codimd.example.com/auth/saml` (replace `https://codimd.example.com` with the base URL of your CodiMD installation)
|
||||

|
||||
6. Leave "Client Signature Required" enabled
|
||||
7. Set Root URL to `https://codimd.example.com` (replace it here also with the base URL of your CodiMD installation)
|
||||
8. Set Valid Redirect URIs to `https://codimd.example.com/auth/saml/callback` (you should also define all other domains of your CodiMD installtion with the suffix `/auth/saml/callback`)
|
||||
9. Set Base URL to `/`
|
||||

|
||||
10. _(optional)_ You can set which Name ID Format should be used
|
||||
|
||||
## Configure CodiMD
|
||||
### Config file
|
||||
You have to put the following block inside your `config.json`:
|
||||
```json
|
||||
"saml": {
|
||||
"issuer": "codimd", // Change to the "Client ID" specified in the Keycloak Client
|
||||
"identifierFormat": "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified",
|
||||
"idpSsoUrl": "https://keycloak.example.org/auth/realms/test/protocol/saml", // replace keycloak.example.org with the url of your keycloak server
|
||||
"idpCert": "/path/to/the/cert.pem",
|
||||
"clientCert": "/path/to/the/key.pem" // this one is optional, see below
|
||||
}
|
||||
```
|
||||
|
||||
### Environment Variables
|
||||
- `CMD_SAML_IDPSSOURL`: `https://keycloak.example.org/auth/realms/test/protocol/saml` (replace keycloak.example.org with the url of your keycloak server)
|
||||
- `CMD_SAML_IDPCERT`: `/path/to/the/cert.pem`
|
||||
- *(optional, see below)* `CMD_SAML_CLIENTCERT`: `/path/to/the/key.pem`
|
||||
- `CMD_SAML_ISSUER`: `codimd` (Change to the "Client ID" specified in the Keycloak Client)
|
||||
- `CMD_SAML_IDENTIFIERFORMAT`: `urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified`
|
||||
|
||||
|
||||
## Client certificate *(optional)*
|
||||
If you want keycloak to be able to verify CodiMD, you hava to create a client certificate. There are two options for this:
|
||||
|
||||
### Create Private Keys for Signing
|
||||
1. Generate the private key and certificate with the following commands:
|
||||
```shell
|
||||
openssl genrsa -out priv.pem 2048
|
||||
openssl req -new -x509 -key priv.pem -out cert.pem
|
||||
```
|
||||
*execute the following steps in keycloak*
|
||||
|
||||
2. Select "Client" in left sidebar
|
||||
3. Go to your CodiMD-Client
|
||||
4. Select the "SAML Keys" tab
|
||||

|
||||
5. Click on "Import"
|
||||
6. Select `Certificate PEM` as "Archive Format"
|
||||
7. Now upload the generated cert.pem (in this case named `cert.pem`)
|
||||

|
||||
8. Click on "Import"
|
||||
9. Move or copy this key (in this case named `key.pem`) and save it to the file specified in `saml.clientCert` property of the CodiMD configuration or in the enviroment-variable `CMD_SAML_CLIENTCERT`
|
||||
|
||||
|
||||
### Convert Private Certificate generated by KeyCloak
|
||||
Instead if generating you own certificate, you can also use the one generated by keycloak.
|
||||
|
||||
1. Select "Client" in left sidebar
|
||||
2. Go to your CodiMD-Client
|
||||
3. Select the "SAML Keys" tab
|
||||

|
||||
|
||||
5. Now click on "Export"
|
||||
6. Here you can select the output format, choose `PKCS12`. You also have to set a password. Choose your own.
|
||||

|
||||
6. Click on "Download" and save the file somewhere on you computer
|
||||
7. You now have to extract the private Key. You can do this with the following command. WHen asked, enter your password.
|
||||
```shell
|
||||
openssl pkcs12 -in keystore.p12 -out key.pem -nocerts -nodes
|
||||
```
|
||||
8. Move or copy this key (in this case named `key.pem`) and save it to the file specified in `saml.idpCert` property of the CodiMD configuration or in the enviroment-variable `CMD_SAML_CLIENTCERT`
|
||||
|
||||
## Use Persistent Identifiers
|
||||
Instead of using the username as the owner-key in the CodiMD database, you can also use a persistent identifier. This allows to change the username, without them loosing access to their notes.
|
||||
|
||||
1. Go to the CodiMD-Client in keycloak. Now enable the option "Force Name ID Format" and select "persistent" as the "Name ID Format".
|
||||

|
||||
2. For codimd to be able to use the username and email configured in keycloak, you have to create the following SAML protocol mappers:
|
||||
2.1. Create a mapper with the type `User Property`. Set the Name, Property and SAML Attribute Name to `username`. Now you can specify a friendly name (for example `Username`)
|
||||

|
||||
2.2 Create a mapper with the type `User Property`. Set the Name, Property and SAML Attribute Name to `email`. Now you can specify a friendly name (for example `E-Mail`)
|
||||

|
||||
|
||||
The configured mappers should look like this:
|
||||

|
||||
|
||||
3. You now have to add the following block to the saml-definition inside your `config.json`:
|
||||
```json
|
||||
"attribute": {
|
||||
"username": "username"
|
||||
"email": "email",
|
||||
}
|
||||
```
|
||||
It you configure CodiMD with enviroment variables, these are the ones you have to set:
|
||||
```bash
|
||||
CMD_SAML_ATTRIBUTE_USERNAME=username
|
||||
CMD_SAML_ATTRIBUTE_EMAIL=email
|
||||
```
|
BIN
docs/images/auth/keycloak_add_client.png
Normal file
After Width: | Height: | Size: 53 KiB |
BIN
docs/images/auth/keycloak_client_overview.png
Normal file
After Width: | Height: | Size: 203 KiB |
BIN
docs/images/auth/keycloak_clients_overview.png
Normal file
After Width: | Height: | Size: 77 KiB |
BIN
docs/images/auth/keycloak_force_idformat.png
Normal file
After Width: | Height: | Size: 5.9 KiB |
BIN
docs/images/auth/keycloak_idp_cert.png
Normal file
After Width: | Height: | Size: 73 KiB |
BIN
docs/images/auth/keycloak_mapper_email.png
Normal file
After Width: | Height: | Size: 52 KiB |
BIN
docs/images/auth/keycloak_mapper_overview.png
Normal file
After Width: | Height: | Size: 30 KiB |
BIN
docs/images/auth/keycloak_mapper_username.png
Normal file
After Width: | Height: | Size: 54 KiB |
BIN
docs/images/auth/keycloak_saml_export_cert.png
Normal file
After Width: | Height: | Size: 177 KiB |
BIN
docs/images/auth/keycloak_saml_export_cert_details.png
Normal file
After Width: | Height: | Size: 23 KiB |
BIN
docs/images/auth/keycloak_saml_import_cert.png
Normal file
After Width: | Height: | Size: 177 KiB |
BIN
docs/images/auth/keycloak_saml_import_cert_details.png
Normal file
After Width: | Height: | Size: 48 KiB |
|
@ -143,6 +143,7 @@ module.exports = {
|
|||
saml: {
|
||||
idpSsoUrl: undefined,
|
||||
idpCert: undefined,
|
||||
clientCert: undefined,
|
||||
issuer: undefined,
|
||||
identifierFormat: 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress',
|
||||
disableRequestedAuthnContext: false,
|
||||
|
|
|
@ -120,6 +120,7 @@ module.exports = {
|
|||
saml: {
|
||||
idpSsoUrl: process.env.CMD_SAML_IDPSSOURL,
|
||||
idpCert: process.env.CMD_SAML_IDPCERT,
|
||||
clientCert: process.env.CMD_SAML_CLIENTCERT,
|
||||
issuer: process.env.CMD_SAML_ISSUER,
|
||||
identifierFormat: process.env.CMD_SAML_IDENTIFIERFORMAT,
|
||||
disableRequestedAuthnContext: toBooleanConfig(process.env.CMD_SAML_DISABLEREQUESTEDAUTHNCONTEXT),
|
||||
|
|
|
@ -16,7 +16,21 @@ passport.use(new SamlStrategy({
|
|||
callbackUrl: config.serverURL + '/auth/saml/callback',
|
||||
entryPoint: config.saml.idpSsoUrl,
|
||||
issuer: config.saml.issuer || config.serverURL,
|
||||
cert: fs.readFileSync(config.saml.idpCert, 'utf-8'),
|
||||
privateCert: config.saml.clientCert === undefined ? undefined : (function () {
|
||||
try {
|
||||
return fs.readFileSync(config.saml.clientCert, 'utf-8')
|
||||
} catch (e) {
|
||||
logger.error(`SAML client certificate: ${e.message}`)
|
||||
}
|
||||
}()),
|
||||
cert: (function () {
|
||||
try {
|
||||
return fs.readFileSync(config.saml.idpCert, 'utf-8')
|
||||
} catch (e) {
|
||||
logger.error(`SAML idp certificate: ${e.message}`)
|
||||
process.exit(1)
|
||||
}
|
||||
}()),
|
||||
identifierFormat: config.saml.identifierFormat,
|
||||
disableRequestedAuthnContext: config.saml.disableRequestedAuthnContext
|
||||
}, function (user, done) {
|
||||
|
|