OpenID Authentication with Keycloak

Version 1.2 by Johannes Wielsch on 2020/08/29 12:39

Read https://extensions.xwiki.org/xwiki/bin/view/Extension/OpenID%20Connect/OpenID%20Connect%20Authenticator/ first!

The steps below assume, that you have a working keycloak installation and the clients can connect to xwiki and keycloak.

First Step

Install the extension mentioned above.

Second Step

Find xwiki.authentication.authclass in xwiki.cfg and comment it out with #-# in the beginning. add below:

xwiki.authentication.authclass=org.xwiki.contrib.oidc.auth.OIDCAuthServiceImpl

Save xwiki.cfg.

Thrid Step

Open xwiki.properties.

Adopt the following to your settings and add this at the end of the file. (Note the bold and undelined parts.)

    oidc.xwikiprovider=https://YOUR-WIKI-ADDRESS/xwiki/oidc
    oidc.endpoint.authorization=https://KEYCLOAK-ADDRESS/auth/realms/REALM/protocol/openid-connect/auth
    oidc.endpoint.token=https://KEYCLOAK-ADDRESS/auth/realms/REALM/protocol/openid-connect/token
    oidc.endpoint.userinfo=https://KEYCLOAK-ADDRESS/auth/realms/REALM/protocol/openid-connect/userinfo
    oidc.scope=openid,profile,email,address
    oidc.endpoint.userinfo.method=GET
    oidc.user.nameFormater=${oidc.user.preferredUsername._clean._lowerCase}
    oidc.user.subjectFormater=${oidc.user.subject}
    # oidc.groups.claim=xwiki_groups
    # oidc.groups.mapping=MyXWikiGroup=my-oidc-group
    # oidc.groups.mapping=MyXWikiGroup2=my-oidc-group2
    # oidc.groups.mapping=MyXWikiGroup2=my-oidc-group3
    # oidc.groups.allowed=
    # oidc.groups.forbidden=
    oidc.userinfoclaims=xwiki_user_accessibility,xwiki_user_company,xwiki_user_displayHiddenDocuments,xwiki_user_editor,xwiki_user_usertype
    # oidc.userinforefreshrate=600000
    oidc.clientid=KEYCLOAK-CLIENT-ID
    oidc.secret=KEYCLOAK-CLIENT-SECRET
    oidc.endpoint.token.auth_method=client_secret_basic
    oidc.skipped=false

Fourth Step
Adopt the following to your settings and import it to keycloak. (Again: Note the bold an underlined parts.)

   {
        "clientId": "CLIENT-ID",
        "name": "CLIENT-NAME",
        "rootUrl": "https://YOUR-WIKI-URL",
        "adminUrl": "https://YOUR-WIKI-URL",
        "baseUrl": "https://YOUR-WIKI-URL",
        "surrogateAuthRequired": false,
        "enabled": true,
        "alwaysDisplayInConsole": false,
        "clientAuthenticatorType": "client-secret",
        "redirectUris": [
            "https://YOUR-WIKI-URL/*"
        ],
        "webOrigins": [
            "https://YOUR-WIKI-URL"
        ],
        "notBefore": 0,
        "bearerOnly": false,
        "consentRequired": false,
        "standardFlowEnabled": true,
        "implicitFlowEnabled": true,
        "directAccessGrantsEnabled": true,
        "serviceAccountsEnabled": true,
        "authorizationServicesEnabled": true,
        "publicClient": false,
        "frontchannelLogout": false,
        "protocol": "openid-connect",
        "attributes": {
            "saml.assertion.signature": "false",
            "saml.force.post.binding": "false",
            "saml.multivalued.roles": "false",
            "saml.encrypt": "false",
            "saml.server.signature": "false",
            "saml.server.signature.keyinfo.ext": "false",
            "exclude.session.state.from.auth.response": "false",
            "saml_force_name_id_format": "false",
            "saml.client.signature": "false",
            "tls.client.certificate.bound.access.tokens": "false",
            "saml.authnstatement": "false",
            "display.on.consent.screen": "false",
            "saml.onetimeuse.condition": "false"
        },
        "authenticationFlowBindingOverrides": {},
        "fullScopeAllowed": false,
        "nodeReRegistrationTimeout": -1,
        "protocolMappers": [
            {
                "name": "Client Host",
                "protocol": "openid-connect",
                "protocolMapper": "oidc-usersessionmodel-note-mapper",
                "consentRequired": false,
                "config": {
                    "user.session.note": "clientHost",
                    "id.token.claim": "true",
                    "access.token.claim": "true",
                    "claim.name": "clientHost",
                    "jsonType.label": "String"
                }
            },
            {
                "name": "Client IP Address",
                "protocol": "openid-connect",
                "protocolMapper": "oidc-usersessionmodel-note-mapper",
                "consentRequired": false,
                "config": {
                    "user.session.note": "clientAddress",
                    "id.token.claim": "true",
                    "access.token.claim": "true",
                    "claim.name": "clientAddress",
                    "jsonType.label": "String"
                }
            },
            {
                "name": "address",
                "protocol": "openid-connect",
                "protocolMapper": "oidc-address-mapper",
                "consentRequired": false,
                "config": {
                    "user.attribute.formatted": "formatted",
                    "user.attribute.country": "country",
                    "user.attribute.postal_code": "postal_code",
                    "userinfo.token.claim": "true",
                    "user.attribute.street": "street",
                    "id.token.claim": "true",
                    "user.attribute.region": "region",
                    "access.token.claim": "true",
                    "user.attribute.locality": "locality"
                }
            },
            {
                "name": "Client ID",
                "protocol": "openid-connect",
                "protocolMapper": "oidc-usersessionmodel-note-mapper",
                "consentRequired": false,
                "config": {
                    "user.session.note": "clientId",
                    "id.token.claim": "true",
                    "access.token.claim": "true",
                    "claim.name": "clientId",
                    "jsonType.label": "String"
                }
            }
        ],
        "defaultClientScopes": [
            "web-origins",
            "role_list",
            "roles",
            "profile",
            "email"
        ],
        "optionalClientScopes": [
            "address",
            "phone",
            "offline_access",
            "microprofile-jwt"
        ],
        "access": {
            "view": true,
            "configure": true,
            "manage": true
        }
    }

After importing this to Keycloak you have to generate a new Client-Secret and put it into xwiki.properties under "oidc.secret=KEYCLOAK-CLIENT-SECRET".

Aim is: In the end, Client-ID and Client-Secret have to match on Keycloak and in xwiki.properties.

Tags:
    

Get Connected