linkwarden / linkwarden

⚡️⚡️⚡️Self-hosted collaborative bookmark manager to collect, organize, and preserve webpages and articles.
https://linkwarden.app
GNU Affero General Public License v3.0
6.75k stars 254 forks source link

SSO/OIDC: Authelia support #382

Open Bykow opened 6 months ago

Bykow commented 6 months ago

Is your feature request related to a problem? Please describe. Noticed that SSO/OIDC support is rather recent on Linkwarden, but it already has an impressive catalogue of supported providers. But Authelia is missing. Among Authentik and Keycloack, Authelia is one of the most popular self hosted solution for SSO/OIDC.

Describe the solution you'd like Add support for Authelia

Describe alternatives you've considered For now, using local users is fine.

cthu1hoo commented 6 months ago

I'm not sure why there are so many specific OIDC providers hardcoded instead of one where you could set common variables like callback URL, etc.

traeblain commented 5 months ago

So I got it working with Authelia by "spoofing" Keycloak.

NEXTAUTH_URL=https://{{domain}}/api/v1/auth
NEXT_PUBLIC_KEYCLOAK_ENABLED=true
KEYCLOAK_CUSTOM_NAME=Authelia
KEYCLOAK_ISSUER=https://{{authelia_domain}}/
KEYCLOAK_CLIENT_ID=linkwarden
KEYCLOAK_CLIENT_SECRET={{client_secret}}

So just like Keycloak except with Authelia's details.

Then in Authelia:

  - id: linkwarden
    description: Linkwarden instance
    secret: '$pbkdf2-sha512...'
    redirect_uris:
      - https://{{linkwarden_domain}}/api/v1/auth/callback/keycloak
    scopes:
      - openid
      - profile
      - email
      - groups

And this worked for me. I will say I use Caddy as my reverse_proxy and I've had some issues with Authelia behind it and trailing /s.

So in my Caddyfile, I have to handle trailing slashes for all my .well-known links. So when any service pings /.well-known/openid-configuration/ it returns a 200 and data instead of a 301 to /.well-known/openid-configuration...a bit annoying... So here is my augmentation from the Authelia documentation.

## It is important to read the following document before enabling this section:
##     https://www.authelia.com/integration/proxies/caddy/#forwarded-header-trust#trusted-proxies
(trusted_proxy_list) {
       ## Uncomment & adjust the following line to configure specific ranges which should be considered as trustworthy.
       # trusted_proxies 10.0.0.0/8 172.16.0.0/16 192.168.0.0/16 fc00::/7
}
auth.example.com {
        handle /.well-known/* {
                uri strip_suffix /
        }
        reverse_proxy authelia:9091 {
                ## This import needs to be included if you're relying on a trusted proxies configuration.
                import trusted_proxy_list
        }
}

Note the handle directive before the reverse_proxy so that it just does it without redirect.

Agreed the solve for this is to support all the required endpoints in a generic OIDC Integration where all the details are indicated as needed.

scottharney commented 5 months ago

hmm. I'm close but I think some bits are missing from the authelia configuration? Do you have values set for grant_types, resopnse_types or response_modes in your linkwarden config stanza? when I try to login I get uthelia | time="2024-01-25T09:13:03-06:00" level=info msg="Access to https://linkwarden.domain/site.webmanifest (method GET) is not authorized to user , responding with status code 302 with location redirect to https://auth.domain?rd=https%3A%2F%2Flinkwarden.domain%2Fsite.webmanifest&rm=GET" method=GET path=/api/verify remote_ip=

also I'm not sure if the issue isn't the client_secret in .env. I'm never sure the correct format to quote that. Is it single quotes? double? does it need the $pbdkf2-sha512 bit like the authelia config or just the string or???

traeblain commented 5 months ago

I'm assuming you've followed the process correctly for setting up Authelia for OIDC and Identity Provider. Authelia OIDC FAQ.

uthelia | time="2024-01-25T09:13:03-06:00" level=info msg="Access to https://linkwarden.domain/site.webmanifest (method GET) is not authorized to user , responding with status code 302 with location redirect to https://auth.domain?rd=https%3A%2F%2Flinkwarden.domain%2Fsite.webmanifest&rm=GET" method=GET path=/api/verify remote_ip=

Is what Authelia logs when you try to access something that is protected. It's saying go to Authelia, get it to log in, then redirect back to the rd if successful. This means your reverse proxy setup wrong if you are getting this log. You are not protecting the location with forward_auth, you are giving Linkwarden an SSO condition.

The entire Linkwarden config for Authelia is above. All of Authelia's defaults work out of the box.

For the secret you need to run the command: authelia crypto hash generate pbkdf2 --variant sha512 --random --random.length 72 --random.charset rfc3986 That will output something like:

~$ authelia crypto hash generate pbkdf2 --variant sha512 --random --random.length 72 --random.charset rfc3986
Random Password: MeK2a1s2qHhv2QUHYIQBKOdlkGA8QWXryHFYeO05H.1a124GNFpitvTnXYGBrTigUbokarbL
Digest: $pbkdf2-sha512$310000$LJmVuuVq1g41MypSupAxhA$uIkfGX6yIt2YY0QOrHcW/d5N9OroR0GERiaBOqA1h9QzVYVhMvHELfVhT8c5zQ29gxeR01tBH5f1Y215tvBpVA

The Random Password goes in KEYCLOAK_CLIENT_SECRET environment variable, no quotes. The Digest goes in Linkwarden's config in Authelia.

secret: '$pbkdf2-sha512$310000$LJmVuuVq1g41MypSupAxhA$uIkfGX6yIt2YY0QOrHcW/d5N9OroR0GERiaBOqA1h9QzVYVhMvHELfVhT8c5zQ29gxeR01tBH5f1Y215tvBpVA'
scottharney commented 5 months ago

Thanks for the help. I did have forwardauth configured with Traefik. The secret generation you provided was helpful to provide relevant entries for those two values. What it ultimately turned out to be was the trailing slash on KEYCLOAK_ISSUER in .env . So now my .env looks like:

NEXT_PUBLIC_CREDENTIALS_ENABLED=false
NEXTAUTH_SECRET={{secret contents}}}
NEXTAUTH_URL=https://linkwarden.my.doma.in/api/v1/auth
NEXT_PUBLIC_KEYCLOAK_ENABLED=true
KEYCLOAK_CUSTOM_NAME=Authelia
KEYCLOAK_ISSUER=https://auth.my.doma.in
KEYCLOAK_CLIENT_ID=linkwarden
KEYCLOAK_CLIENT_SECRET={{authelia client secret content }}
POSTGRES_PASSWORD={{pg secret content}}

And the client definition in authelia's config.yml is

    - id: linkwarden
      description: Linkwarden
      secret: '$pbkdf2-sha512${{client secret digest content}}'
      public: false
      authorization_policy: two_factor
      audience: []
      scopes:
        - openid
        - profile
        - email
        - groups
      redirect_uris:
        - https://linkwarden.my.doma.in/api/v1/auth/callback/keycloak
      userinfo_signing_algorithm: none

Lastly in my docker-compose.yml for linkwarden, the relevant traefik labels are:

      - traefik.http.routers.linkwarden.rule=Host("linkwarden.my.doma.in")
      - traefik.http.routers.linkwarden.entrypoints=web
      - traefik.http.routers.linkwarden.middlewares=https-redirect
      - traefik.http.routers.linkwarden_https.rule=Host("linkwarden.my.doma.in")
      - traefik.http.routers.linkwarden_https.tls=true
      - traefik.http.routers.linkwarden_https.entrypoints=websecure
      - traefik.http.routers.linkwarden_https.tls.certresolver=le
      - traefik.http.routers.linkwarden_https.middlewares=authelia@docker

These are largely common to my docker-compose file and the forwardauth configuration is what is standard for a traefik+authelia containerized setup

Mag1cByt3s commented 4 months ago

Second this. On the one hand there is loads of support for OIDC providers but on the other hand no support for the most popular SSO solution in the self-hosted community right now which is still authelia.