Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
A guide for configuring Keycloak as a authentication provider in WikiJS | https://wiki.js.org | Feature request for adding this to the docs: https://requarks.canny.io/wiki/p/keycloak-auth-docs-proposal-for-a-guide-written

Feature request for adding this to the docs on requarks.canny.io


Keycloak

Keycloak is an Open Source Identity and Access Management solution for modern Applications and Services.

Relevant information

Setup

Create Keycloak strategy instance on WikiJS

  1. In the Administration area of your wiki, click on Authentication in the left navigation menu
  2. Click on + ADD STRATEGY, scroll down and select Keycloak
  3. Click Apply (Just to make sure the instance ID will not be regenerated if the page is reloaded. This produces an error in the logs, so it might display an error on the frontend too in the future. If so just skip this step and double check the ID when setting it up)
  4. Go to the bottom of the page and copy/note the Callback URL / Redirect URI
  5. We will fill out the rest after setting up the Keycloak client

Creating a Keycloak client

  1. At the Keycloak administration page, go to the Clients menu, and click Create button on the right
  2. Enter a Client ID, for example wikijs (You wil need the Client ID later)
  3. Select openid-connect as Client Protocol
  4. And Root URL is the base URL to Wikijs (for example https://wiki.example.com)
  5. Click Save
  6. Change Access Type to confidential
  7. Enter the Valid Redirect URIs, which is the Callback URL / Redirect URI from WikiJS (ex. https://wiki.example.com/login/d03f689b-0dd0-44d6-90ca-6386ec41d799/callback, or just the path /login/{GUID}/callback)
  8. Set Base URL to the same as Root URL
  9. Set Web Origins to +, which means to use the URIs in the Valid Redirect URIs entry.
  10. Now click Save at the bottom of the page
  11. Go to the Credentials tab and copy the Secret (You will need this one later too)

Configure the Keycloak strategy in Wiki.js

  1. If you're not already there. Go to the Administration area of your wiki, click on Authentication in the left navigation menu
  2. Click on Keycloak
  3. Enter the Host, which is the domain (incl. the scheme) of your Keycloak server (Example: https://keycloak.example.com)
  4. Enter the Realm, which is the realm you are using in Keycloak (Default is: master)
  5. Enter the Client Id, which is the Client ID from Keycloak
  6. Enter the Client Secret, which is the Secret from Keycloak
  7. Enter the Authorization Endpoint URL, which is https://keycloak.example.com/auth/realms/master/protocol/openid-connect/auth
  8. Enter the Token URL, which is https://keycloak.example.com/auth/realms/master/protocol/openid-connect/token
  9. Enter the User Info URL, which is https://keycloak.example.com/auth/realms/master/protocol/openid-connect/userinfo
  10. If you want the user to be logged out of Keycloak when logging out of WikiJS, enable Logout from Keycloak on Logout
  11. Enter the Logout Endpoint URL, which is https://keycloak.example.com/auth/realms/master/protocol/openid-connect/logout
  12. Check Allow self-registration to enable the Keycloak login button, and auto create users as they login for the first time.
  13. Remember to add a group with at least read permissions in the Assign to group list
  14. Click Apply in the top-left corner and try to login

Seamless login

If the login worked, you can enable Bypass Login Screen under the Security tab in the left navigation menu.
Make sure the Keycloak provider is at the top of the list in the Authentication tab.

@nicosalvadore
Copy link

nicosalvadore commented Mar 22, 2021

@Sherex, if I follow your instructions, the keycloak login page can't properly load and I get the following keycloak error :
Invalid parameter: redirect_uri

  • I have to modifiy Valid Redirect URIs to something like http://wiki.example.com/login*, the callback URL may have changed ?
    I'm on keycloak v12.0.4 and Wikijs v2.5.170.

  • After having changed the valid redirect URI, I get the keycloak login prompt and can try to log in, but I receive an Wikijs error :
    Failed to obtain access token

The token URL configured on wikijs seems correct though.

Thank you for your insights :)

@iamniels
Copy link

iamniels commented Mar 22, 2021

@nicosalvadore In Keycloak client settings, you need to set Valid redirect URIs to the url which is listed below the Keycloak settings in WikiJS. I guess + will work as well.

I just configured WikiJS and Keycloak using this guide and I'm able to login. Thanks @Sherex!

@Sherex
Copy link
Author

Sherex commented Mar 26, 2021

Hey again @nicosalvadore,

I checked a little and when a new authentication strategy instance is created it is now delegated a guid, instead of a name (ex. keycloak).
I guess this happened when they made it possible to have several instances of a strategy.

You can find that and other useful information at the bottom of the authentication page of WikiJS, when you have a strategy selected.

In Root URL and Base URL use https://wiki.example.com.

And then a Valid Redirect URIs entry with /login/73ff0ebd-4c04-4c17-994b-4ccc7ff59999/callback (Or whatever guid that instance got)

Example (Change the GUID):
keycloak_screenshot

(I will update the guide in a bit)

Hope this helps! :)

@Sherex
Copy link
Author

Sherex commented Mar 26, 2021

Awesome @iamniels!

Glad I could help :)

@xcojonny
Copy link

xcojonny commented Apr 11, 2021

Hello,

thanks for the guide!
I followed your guide. Unfortunately I'm not able to make it till the end.
When I log in via keycloak I'm redirected to the wiki page (https://wiki.domain.com/login/GUID/callback?session_state=shortcode&code=longcode) which says:

Wikijs
Oops, something went wrong...

Do you have any suggestions of what I have possibly made wrong?

@Sherex
Copy link
Author

Sherex commented Apr 11, 2021

Hey @xcojonny,

Hmm, I tried to reproduce it, but I was only able to get a error page when I had the wrong secret. Did it say "Invalid client secret" too?
wikijs-error-screenshot

I would check the Wiki-js logs if there are any errors there.
There is a logLevel setting in the config file, try setting it to "debug", and see if there is any interesting output during a login. (feel free to create a gist/pastebin of the log output and post the link here).

@Sherex
Copy link
Author

Sherex commented Apr 11, 2021

Ah, I managed to get a Oops, something went wrong... message by having a invalid Token Endpoint URL and/or User Info Endpoint URL in WikiJS.

Check if there is a copy/paste mistake in either of those fields. :)

@xcojonny
Copy link

xcojonny commented Apr 11, 2021

Thanks a lot for your super fast reply!!!
Secondly, sorry for wasting your time. Your hint was correct. I had a little mistake in the url in the wikijs settings. In the wikijs logs was no error.

again: thanks a lot :)

@Sherex
Copy link
Author

Sherex commented Apr 11, 2021

No worries, glad I could help! :)

@luismanson
Copy link

luismanson commented Jul 6, 2021

hmm.. i have this issue, "Failed to obtain access token". Keycloak show login ok, I have enabled debug in wikijs, but there are no error in logs :\

@Sherex
Copy link
Author

Sherex commented Jul 6, 2021

Hey @luismanson

I'm not able to check right now, but I would double check the Token Url from step 8. Make sure it's your domain.

You can also check if the URL is correct by just pasting it in the browser. You should get a 500 internal server error from Keycloak, not a 404 page not found.
The browser might show something like "Address not found", then it's probably the domain part that's wrong.

Hope this helps! 🙂

@luismanson
Copy link

luismanson commented Jul 7, 2021

After copy and paste of all urls, worked. Thanks!

@panostzemis
Copy link

panostzemis commented Jul 8, 2021

Hello there, first of all thank you for your guide.

I still have error to login... I get the
Oops, something went wrong... You are not authorized to login.

image
image

The redirection seems to be fine with the callback provided by wikijs.
Provided call back: https://wiki.etias-dev-cluster-dd44761594587af0cac9f15b900dfca4-i000.eu-de.containers.appdomain.cloud/login/acdc38ef-c673-4449-9eb1-f0f8a9836188/callback

Url with the Oops Message: https://wiki.etias-dev-cluster-dd44761594587af0cac9f15b900dfca4-i000.eu-de.containers.appdomain.cloud/login/acdc38ef-c673-4449-9eb1-f0f8a9836188/callback?session_state=bd3cf89d-64e5-4189-aade-1124251887f8&code=03d84ea6-8c55-4db4-bf48-f3eb6d72cc2e.bd3cf89d-64e5-4189-aade-1124251887f8.df365845-dd3a-40e9-a448-d00ca34ff942

I have tried also with another client and triple-checked everything but I am missing something I guess...
I will be glad if you could help me.

@xcojonny
Copy link

xcojonny commented Jul 8, 2021

Hej,

maybe your keycloak settings are not correct. Have look at mine.
ADEDC76F-40DB-4201-A6DA-577884EC4778
D0E0E908-71D5-4E9B-AE2E-845019264AE6

@panostzemis
Copy link

panostzemis commented Jul 9, 2021

Thank you @xcojonny,

It seems that I had to activate the Allow Self-Registration even if I have that user in keycloak. I guess it does not link the existing Wiki local users with the keycloak users... thank you 👍

@chikamichi
Copy link

chikamichi commented Sep 14, 2021

Hi, thank you for the guide!

Would you consider (you or someone else) sending a PR for https://github.com/Requarks/wiki-docs/tree/master/auth?

@Sherex
Copy link
Author

Sherex commented Sep 15, 2021

Hi @chikamichi!

You're welcome :)
I created a PR here: requarks/wiki-docs#28

@ajl000
Copy link

ajl000 commented Oct 3, 2021

Many thanks for this
Can Wiki.js be dynamically tied to a user's keycloak groups.
For example, I would would like users of a particular keycloak group to be able to view some documents. If the user is no longer a member of the group on keycloak then the user should not have access.

(Alternatively could pass the username and groups in a header from Traefik Forward Auth ... does any one know if this is possible).

@ajl000
Copy link

ajl000 commented Oct 3, 2021

Looks like something is planned https://js.wiki/feedback/p/group-mapping

@Sherex
Copy link
Author

Sherex commented Oct 3, 2021

Hey @ajl000!

Yeah I don't think it's possible atm, but this might be a workaround.

I haven't tested, but it might be possible to create multiple auth strategies for Keycloak (or LDAP), which auto assigns different groups.

So in Keycloak you create two or more "clients" for Wiki.JS. Then restricts users to use the correct ones (possibly through roles). These clients then gets added to Wiki.JS as different auth strategies with different auto assigned groups. Which then in turn has different permissions.

And in LDAP it would mostly the same, but instead of different "clients" in Keycloak. The search filter would be do an "AND" check for the right group with username.

It is not a good solution and Keycloak would ask the user's permission when it authenticates through a new client for the first time. And it would not update group membership if that changes from the source (Keycloak or LDAP).

But this may work for some scenarios. :)

@Sherex
Copy link
Author

Sherex commented Oct 3, 2021

Looks like something is planned js.wiki/feedback/p/group-mapping

Nice, upvoted! :)

@Dreamerset
Copy link

Dreamerset commented Jan 18, 2022

Hi @Sherex,

Great work on the setup guide!

I followed it by the letter using latest wiki.js and keycloak docker images deployed on a single host.
Needless to say both containers work with localhost ports setup at:
wikijs - http://localhost:3000
keycloak - http://localhost:8888
(click on the images bellow to see in full size)
The keycloak client setting:

And the credentials:

The wikijs authentication strategy setting for keycloak:

Wiki JS at http://localhost:3000/login login looks good:

As well as keycloak provided login at http://localhost:8888/auth/realms/sso/protocol/openid-connect/auth?response_type=code&redirect_uri=http%3A%2F%2Flocalhost%3A3000%2Flogin%2F23d19cb0-ba79-4e1a-9b7d-95760f029962%2Fcallback&client_id=wiki

Register a user is successful in keycloak, but never gets to wikijs DB despite the enabled option in wikijs at: http://localhost:8888/auth/realms/sso/login-actions/registration?client_id=wiki&tab_id=bO52bCRi-b8

The error comes at the next address after hitting Register button and being redirected to:
http://localhost:3000/login/23d19cb0-ba79-4e1a-9b7d-95760f029962/callback?session_state=e4e6b9a2-1eeb-43cf-9394-6a7c7758a7fc&code=da85fc67-1157-497b-9234-4bb4e22423f8.e4e6b9a2-1eeb-43cf-9394-6a7c7758a7fc.3f631903-6dc7-40cd-8453-6cdbe8dfd8f4

Request Headers:

GET /login/23d19cb0-ba79-4e1a-9b7d-95760f029962/callback?session_state=e4e6b9a2-1eeb-43cf-9394-6a7c7758a7fc&code=da85fc67-1157-497b-9234-4bb4e22423f8.e4e6b9a2-1eeb-43cf-9394-6a7c7758a7fc.3f631903-6dc7-40cd-8453-6cdbe8dfd8f4 HTTP/1.1
Host: localhost:3000
Connection: keep-alive
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
DNT: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: same-site
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
sec-ch-ua: " Not;A Brand";v="99", "Google Chrome";v="97", "Chromium";v="97"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9

Response Headers:

HTTP/1.1 500 Internal Server Error
X-Frame-Options: deny
X-XSS-Protection: 1; mode=block
X-Content-Type-Options: nosniff
X-UA-Compatible: IE=edge
Referrer-Policy: same-origin
Content-Language: en
Content-Type: text/html; charset=utf-8
ETag: W/"7b0-6pE/79w2XkNq+LbBR0B6V7BM0Y0"
Vary: Accept-Encoding
Content-Encoding: gzip
Date: Tue, 18 Jan 2022 11:21:48 GMT
Connection: keep-alive
Keep-Alive: timeout=5
Transfer-Encoding: chunked

Response:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta charset="UTF-8">
    <meta name="viewport" content="user-scalable=yes, width=device-width, initial-scale=1, maximum-scale=5">
    <meta name="theme-color" content="#1976d2">
    <meta name="msapplication-TileColor" content="#1976d2">
    <meta name="msapplication-TileImage" content="/_assets/favicons/mstile-150x150.png">
    <title>Error | Wiki.js</title>
    <meta name="description">
    <meta property="og:title" content="Error">
    <meta property="og:type" content="website">
    <meta property="og:description">
    <meta property="og:image">
    <meta property="og:url" content="http://localhost:3000/login/23d19cb0-ba79-4e1a-9b7d-95760f029962/callback">
    <meta property="og:site_name" content="Wiki.js">
    <link rel="apple-touch-icon" sizes="180x180" href="/_assets/favicons/apple-touch-icon.png">
    <link rel="icon" type="image/png" sizes="192x192" href="/_assets/favicons/android-chrome-192x192.png">
    <link rel="icon" type="image/png" sizes="32x32" href="/_assets/favicons/favicon-32x32.png">
    <link rel="icon" type="image/png" sizes="16x16" href="/_assets/favicons/favicon-16x16.png">
    <link rel="mask-icon" href="/_assets/favicons/safari-pinned-tab.svg" color="#1976d2">
    <link rel="manifest" href="/_assets/manifest.json">
    <script>
      var siteConfig = {
        "title": "Wiki.js",
        "theme": "default",
        "darkMode": false,
        "lang": "en",
        "company": "",
        "contentLicense": "",
        "logoUrl": "https://static.requarks.io/logo/wikijs-butterfly.svg"
      }
      var siteLangs = []
    </script>
    <link type="text/css" rel="stylesheet" href="/_assets/css/app.900133827bec651b1efc.css">
    <script type="text/javascript" src="/_assets/js/runtime.js?1640560983"></script>
    <script type="text/javascript" src="/_assets/js/app.js?1640560983"></script>
  </head>
  <body>
    <div class="is-fullscreen" id="root">
      <div class="app-error">
        <a href="/">
          <img src="/_assets/svg/logo-wikijs.svg">
        </a>
        <strong>Oops, something went wrong...</strong>
        <span>Failed to obtain access token</span>
      </div>
    </div>
  </body>
</html>

Guidance, hints, comments on the above would be highly appreciated as I had exhausted my capacity to debug the situation.
Thank you.

@Sherex
Copy link
Author

Sherex commented Jan 18, 2022

Hello @Dreamerset

Thank you! :)


Debugging

I think I was eventually able to reproduce it in two fresh docker containers for WikiJS and Keycloak running locally. And for me at least, it turned out to be the resolving of localhost inside a docker container resolves to the local IP of the container not the host.

Which means when the wiki-js container asks localhost:8080 for an access-token (after the login/registration flow) it actually asks itself on a port where there's nothing running. WikiJS uses passport-js for external authentication and @exlinc/keycloak-passport which is a wrapper around passport-oauth2 for authenticating agaist Keycloak.

Not sure exactly where, but somewhere along the way the error message is dropped and just Failed to obtain access token is returned.
It might be dropped here: passport-oauth2/lib/strategy.js#L178.

I logged the error from that function which reported the message below, then it made sense.

Error: connect ECONNREFUSED 127.0.0.1:8080
    at TCPConnectWrap.afterConnect [as oncomplete] (node:net:1161:16) {
  errno: -111,
  code: 'ECONNREFUSED',
  syscall: 'connect',
  address: '127.0.0.1',
  port: 8080
}

Fix

If you want to run it locally, you should replace localhost with the host's IP in WikiJS General settings menu and the Keycloak authentication strategy and do a restart of the WikiJS container.
And do the same for the urls in Keycloak wiki client settings.

Optionally you can also put the containers in the same docker network and use the container-name as the hostname. But only for the URLs the WikiJS backend uses.
Which i think is:

  • Token Endpoint URL
  • User Info Endpoint URL
  • Maybe the Host, but not sure.

Thank you for the screenshots and details! :D

@Dreamerset
Copy link

Dreamerset commented Jan 19, 2022

@Sherex,
Yep, it worked. Swapping "localhost" with its local network IP address made it.
It is sad that my subconsciousness was actively rejecting that scenario even it was fully aware of it, so I never tried it.
Thank you very much for taking the time to research and respond.


The screenshots and details are just an expression of an attempt to compensate helplessness. :)

@Sherex
Copy link
Author

Sherex commented Jan 20, 2022

Hi @Dreamerset

Awesome!
Hehe, know that feeling, I have done the exact same thing before, so when I saw the ECONNREFUSED 127.0.0.1:8080 the problem was suddenly so obvious. But hey, we can't think of everything at all times, so it's nice to be able to ask for help if we're stuck. :)

You're welcome, glad I could help!

@vogelfreiheit
Copy link

vogelfreiheit commented Jun 7, 2022

I'm observing the same problem as @Dreamerset and it isn't a host resolution problem (it's an external host, and I can curl the URL successfully). Gets "Failed to obtain access token". My configuration matches his.

@mister2d
Copy link

mister2d commented Jul 1, 2022

I'm observing the same problem as @Dreamerset and it isn't a host resolution problem (it's an external host, and I can curl the URL successfully). Gets "Failed to obtain access token". My configuration matches his.

I'm having the same issue. External host and using the same configuration.

I logged the error from that function which reported the message below, then it made sense.

@Sherex how were you able to do this? I am stuck. I am using an internal PKI certificate so I'm thinking the issue may be an SSL issue. Guessing actually.

Setting logLevel: debug in the config.yml does nothing. Logs still output at theinfo level.

@tompauwels1500
Copy link

tompauwels1500 commented Jul 18, 2022

Same problem as @mister2d
Can not get the logging to anything other than info.

@Sherex Can you tell us how to get more logging to troubleshoot?

@MaxwellPSanders
Copy link

MaxwellPSanders commented Aug 19, 2022

I am using Keycloak 19 and Wiki.js 2.5 and I'm having the same issue where it will redirect and be able to get a session with keycloak.
That said, I also get the "Failed to obtain access token" message.
If anyone has figured this out, I appreciate your help!

@chriseaton
Copy link

chriseaton commented Sep 1, 2022

I managed to go through this and found a solution to the "Failed to obtain access token" and invalid username / password, despite a correct configuration in both KeyCloak and Wiki.js. Wiki.js is written in node.JS and under the hood uses passport. Node will, by default, reject any self-signed SSL certificates. The problem is passport does throw this error, but as it bubbles up through wiki.js' error stack - it turns into a much more vague/incorrect "Failed to obtain access token" error.

There are 2 resolutions (only 1 is needed):

  1. Tell node to ignore the self-signed cert. If you are running a self-signed certificate in this case, you'll need to use the environmental variable and value NODE_TLS_REJECT_UNAUTHORIZED=0 on your wiki.js host/docker container to tell node to stop worrying about the self-signed certificate.
  2. Introduce your custom CA The other alternative is to hook up your custom/internal CA to the wiki.js host cert chain so the self-signed certificate becomes "valid" - also see NODE_EXTRA_CA_CERTS for this option if you don't want to mess with the host CA cert chains.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment