TandoorRecipes / recipes

Application for managing recipes, planning meals, building shopping lists and much much more!
https://docs.tandoor.dev
Other
5.37k stars 571 forks source link

How to get OpenID working with Tandoor? #970

Closed Akruidenberg closed 2 years ago

Akruidenberg commented 2 years ago

Issue

Some time ago, i''ve asked some help at the Authentik Github for using OpenID with Traefik. I'm using Authentik for SSO. However, It did not work. Now with more research, the ENV is finally parsing, However, nothing changed. Are there more steps required for OpenID? Are there people who get OpenID working with Tandooor? @vabene1111 I've posted more info this time. Other providers like Authelia now also supporting OpenID, so examples for this are great too.

Setup Info

Version: 0.17.2 OS: OMV 5 (Debian)

ENV File:

DB_ENGINE=django.db.backends.postgresql
POSTGRES_HOST=recipes-postgres
POSTGRES_PORT=5432 
POSTGRES_PASSWORD=secret
STICKY_NAV_PREF_DEFAULT=1
POSTGRES_USER=recipes
POSTGRES_DB=recipes         
SECRET_KEY=secret
REVERSE_PROXY_AUTH=0
COMMENT_PREF_DEFAULT=1
GUNICORN_MEDIA=0
ALLOWED_HOSTS=*
COMMENT_PREF_DEFAULT=1
MEDIA_URL=/media/
STATIC_URL=/static/
SHOPPING_MIN_AUTOSYNC_INTERVAL=5
DEBUG=0
TIMEZONE=Europe/Amsterdam
SOCIALACCOUNT_PROVIDERS={ 'openid': { 'SERVERS': [ {'id':'authentik', 'name':'authentik', 'openid_url':'https://authentik.domain.com/application/o/tandoor/"'}, ]}}
  nginx-recipes:
    image: nginx:mainline-alpine
    container_name: nginx-recipes
    hostname: nginx-recipes
    restart: unless-stopped
    networks:
      - reverseproxy  
      - intern
    security_opt:
      - no-new-privileges:true         
    volumes:
      - recipes-nginx:/etc/nginx/conf.d:ro
      - recipes-config:/static     
      - recipes-media:/media
    labels:
      traefik.enable: "true"
      ## HTTP Routers
      traefik.http.routers.recipes-rtr.entrypoints: https
      ## Middlewares
      traefik.http.routers.recipes-rtr.middlewares: chain-no-auth@file  
      ## HTTP Services
      traefik.http.routers.recipes-rtr.service: recipes-svc
      traefik.http.routers.recipes-rtr.rule: Host(`recept.$DOMAINNAME`)      
      traefik.http.services.recipes-svc.loadbalancer.server.port: 80
      ## watchtower      
      com.centurylinklabs.watchtower.enable: "true"    
      diun.enable: "true"       

  recipes:
    image: vabene1111/recipes:0.17.2
    container_name: recipes
    restart: unless-stopped
    hostname: recipes
#    user: $PUID:$PGID
    networks:
      - reverseproxy
      - intern
    security_opt:
      - no-new-privileges:true   
    volumes:
      - recipes-config:/opt/recipes/staticfiles
      - recipes-nginx:/opt/recipes/nginx/conf.d
      - recipes-media:/opt/recipes/mediafiles    
    depends_on:
      - recipes-postgres
      - nginx-recipes
    env_file:
        - ./recipes.env 
    labels:
## watchtower      
      com.centurylinklabs.watchtower.enable: "true"       
      diun.enable: "true"       

  recipes-postgres:
    image: postgres:12-alpine
    container_name: recipes-postgres
    restart: unless-stopped
    hostname: recipes-postgres
#    user: $PUID:$PGID  
    env_file:
      - ./recipes-postgres.env 
    networks:
      - intern   
    security_opt:
      - no-new-privileges:true        
    secrets:
        - recipes_postgres  
    volumes:
      - recipes-postgres:/var/lib/postgresql/data    
      - $BACKUPDIR/recipes:/backup      
    environment:
      TZ: $TZ
    ## watchtower
    labels:
      com.centurylinklabs.watchtower.enable: "false"     
      diun.enable: "true"      

Other relevant information regarding your problem (proxies, firewalls, etc.)

vabene1111 commented 2 years ago

Hi, thanks for the detailed report.

Altough I have never tested open ID in particular it should be supported by using the allauth open ID provider.

See this for configuration details https://django-allauth.readthedocs.io/en/latest/providers.html#openid

And also the docs for how to configure it in tandoor https://docs.tandoor.dev/features/authentication/#allauth

It's not the most intuitive setup but you usually only do it once at installation so I think it should be fine. If you need help at any particular point let me know and maybe we could also include some examples in the docs so feel free to post your working config when you are done (without any password and urls of course)

Akruidenberg commented 2 years ago

@BeryJu provides a example for this. Env is parsed. i've edited the site url in Tandoor. I don't know what to do next.

vabene1111 commented 2 years ago

ok i see

how have you configured the site in the django admin?

What is the error you are getting? is there no button showing up or are you getting an error message during login or what is the problem ?

Akruidenberg commented 2 years ago

Maybe using https://github.com/mozilla/mozilla-django-oidc would be an option? Looks promising @vabene1111

vabene1111 commented 2 years ago

as allauth is already capable of working with openid i do not see why another package providing openID capability would be necessary.

Adding more dependencies is usually decremental to performance, maintainability and security and thus i do not want to add anything if not necessary. If you detail your issue and answer my questions i might be able to help you get open ID working for your setup and if we determine in this process that allauth is not capable of handling your use case we can evaluate whether or not adding an additional package might make sense.

Akruidenberg commented 2 years ago

Ok. I'm not redirecting to authentik with the parsed env. Setting the site in Tandoor to Tandoor domain itself. No strange things in the Tandoor logs. Looks like it need more configuration.

I'm not familiar with Djanjo anyway. All my other services are working fine.

vabene1111 commented 2 years ago

hmm interesting, i am not familiar with open ID so i dont really know what you "usually" need to configure, i just know that allauth has proven to work very well for most authentication related needs.

I will need to configure a tandoor instance with some open ID provider (i see google has one for example) and see if maybe our login page lacks a button or an input field or something ..

BeryJu commented 2 years ago

There needs to be some way to configure a Client ID and Client Secret, See image

vabene1111 commented 2 years ago

you can do that simply by going to the django admin or by defining those directly as shown on this example in the .env file

BeryJu commented 2 years ago

Oh yeah I forgot that you can do that in the django admin, I haven't used allauth in a while.

vabene1111 commented 2 years ago

so i have been playing with openid for half an hour or so and trying to understand what it actually does. Can you refer me to any website that supports open ID login ? just so i could ty it out so that i can gain an understanding on how it should work

Terminatorthre commented 2 years ago

I am currently having the same issue but I think I got one step further than @Akruidenberg According to https://docs.tandoor.dev/features/authentication/ two settings need to be defined in the ENV file.

Unfortunately it seems the providers are not included in the docker image as I only receive " ModuleNotFoundError: No module named 'allauth.socialaccout'" when I try to start the container. As of my limited knowledge of docker and django, I have no idea how to install the modules inside the image. Maybe @vabene1111 can assist with that?

vabene1111 commented 2 years ago

I am sure that I can fix the module issues but I need an answer to my previous question as I was not able (at least within some time limit) to get open ID working at all on any website. If you could refer me to any public services which I could use to actually understand this topic I am sure that we will be able to get this working in tandoor as well

samip5 commented 2 years ago

I am sure that I can fix the module issues but I need an answer to my previous question as I was not able (at least within some time limit) to get open ID working at all on any website. If you could refer me to any public services which I could use to actually understand this topic I am sure that we will be able to get this working in tandoor as well

I believe Gitlab supports it.

ciphermenial commented 2 years ago

I've been trying to make it work with keycloak but am having no luck. When attempting to enable keycloak auth in the .env file recipes no longer starts.

I have keycloak working with other systems like BookStack and Apache Guacamole.

Terminatorthre commented 2 years ago

Yep, exactly what I tried as well. ModuleNotFoundError: No module named 'allauth.socialaccout'" as I mentioned earlier. We would need an image with the required modules included, then we could continue to test.

vabene1111 commented 2 years ago

interestingly i enabled said modules and they were present, thats why i needed something to test with but google and some other pages i tried failed to give me working credentials or i was just to stupid to get it working (google wanted all kinds of stuff in the dev console etc.).

The thing is i still lack the understanding of how this works and cant seem to find an answer online. Allauth (the django package responsible for openid) gives me the option to enter a URL to perform authentication. But what URL do i need to add there for e.g. Gitlab ? just gitlab.com or does the URL need to contain a secret (i would think so). I created a Gitlab application and enabled the "email" permission so that OpenID should work, i got a secret and redirect URL setup but i have no idea where to go from here.

How does this usually work? is there any public page i can just go to to try this out ? or does one of you want to hop into a discord call or so with me and show it to me because i just cant seem to find any proper documentation on this, its all either oauth, super old or straitup unclear.

ciphermenial commented 2 years ago

I got it working by editing the settings.py with what I needed. When I tried to use the .env file it failed.

Under INSTALLED_APPS I added the line 'allauth.socialaccount.providers.keycloak',

I also replaced this section:

try:
    SOCIALACCOUNT_PROVIDERS = ast.literal_eval(
        os.getenv('SOCIALACCOUNT_PROVIDERS') if os.getenv('SOCIALACCOUNT_PROVIDERS') else '{}')
except ValueError:
    SOCIALACCOUNT_PROVIDERS = json.loads(
        os.getenv('SOCIALACCOUNT_PROVIDERS').replace("'", '"') if os.getenv('SOCIALACCOUNT_PROVIDERS') else '{}')

With this:

SOCIALACCOUNT_PROVIDERS = { 
    'keycloak': {
        'KEYCLOAK_URL': 'https://keycloak.domain.com/auth', 
        'KEYCLOAK_REALM': 'master'
    }
}

I was then able to log into the django administration web ui and add a social application. I was now able to select keycloak from a dropdown for the Provider:. I then entered the client ID and key I had configured in KeyCloak and it worked as expected.

ciphermenial commented 2 years ago

I read through everything a bit more and discovered my mistake was all to do with formatting.

My .env file now looks like this and everything is working:

SOCIAL_PROVIDERS=allauth.socialaccount.providers.keycloak

SOCIALACCOUNT_PROVIDERS={"keycloak":{"KEYCLOAK_URL":"https://keycloak.domain.com/auth","KEYCLOAK_REALM":"master"}}

And I set the settings.py back to default.

vabene1111 commented 2 years ago

awesome, glad this works and thanks for posting your config 👍 the thing is that there is another provider "OpenID" which is independet of all other providers and i think some people are trying to get that to work.

ciphermenial commented 2 years ago

Keycloak is using OpenID for the connection. I think the Keycloak" provider simplifies configuration. In the case of using OpenID in my configuration I believe it would look something like this:

SOCIAL_PROVIDERS=allauth.socialaccount.providers.openid
SOCIALACCOUNT_PROVIDERS = {'openid':{'SERVERS':[dict(id='keycloak',name='Keycloak',openid_url='https://keycloak.domain.com/auth/realms/master/protocol/openid-connect/auth'),]}}
Akruidenberg commented 2 years ago

Any progress?

tomlawesome commented 2 years ago

@Terminatorthre Did you get this working at all with Authentik? I've tried again for another few hours and still can't get it to work.

Authentik itself works fine with lots of other openID providers.

Terminatorthre commented 2 years ago

@tomlawesome unfortunately not. I am therefore evaluating Nextcloud Cookbook as an alternative

smilerz commented 2 years ago

the ironic thing is authentik is built on django - you'd think they'd have some documentation on how to set it up.

Akruidenberg commented 2 years ago

We need a more general implementation, not Allauth @vabene1111, is it possible to make an test image with another library? Portainer for example is working fine with Authentik and OpenID.

smilerz commented 2 years ago

We need a more general implementation, not Allauth @vabene1111, is it possible to make an test image with another library? Portainer for example is working fine with Authentik and OpenID.

If you believe that there is a better authentication backend feel free to create a PR.

vabene1111 commented 2 years ago

somebody just needs to help me understand this system, thats all there needs to happen. I wrote down the questions in this issue but i simply cannot get any of those public open ID providers to work and i dont want to spend the time setting this all up and understanding this because the resources documenting this which i have found so far are simpyl not good or contradict each other all the time

If somebody who actually know how this web standard "open ID" works and has a working auth setup i can work together with them to get alluath to work. I am pretty sure the most common and largest django auth framework will likely not have this fundamental feature broken, its likely just a configuration issue.

vabene1111 commented 2 years ago

but sure, feel free to build yourself an image with a different library and play around, maybe there is something broken in allauth

danaelg commented 2 years ago

Hi, I managed to go further. By attaching a shell to the web_recipes container I discovered that changes in the .env file were not taken into account without completely deleting the containers.

This helped me to find the right syntax of the SOCIALACCOUNT_PROVIERS variable which for me is :

SOCIALACCOUNT_PROVIDERS={'openid': {'SERVERS': {'id': 'authentik', 'name': 'authentik', 'openid_url': '<authentik FQDN>/application/o/tandoor',}}}

then after removing and restarting the containers I created the social application in the django admin panel as describe in the documentation.

A new button "Connect with OpenID" appears on the login screen, but when I click on it I'm not redirected to my authentik server :

image

The url of this page is <tandoor FQDN>/accounts/openid/login/?process=login&next=%2Fsearch%2F

vabene1111 commented 2 years ago

ok so you do need to put a specific open ID url into the settings. And if you paste your open ID into the box and click redirect does not work ?

Do the logs say anything.

Would you mind giving me a working open ID account on your instance (without any access rights to any of your applications) so that i can use it to test how this works ? (you could mail it to me to info@tandoor.dev)

danaelg commented 2 years ago

When accessing this page I put the same URL I set up in the SOCIALACCOUNT_PROVIDERS variable through the openid_url attribute.

When I click on sign in I get a HTTP 500 error. I can't find any details on that error. The command docker-compose logs doesn't say so much...

If you don't mind, I would like to go a bit further in the troubleshooting before giving you a working credential on my instance.

vabene1111 commented 2 years ago

ok just saw your email, thank you. I will try to look into this, with what you posted so far i am beginning to get a better understanding of how open id is supposed to work. All examples used open ID without specifying any servers so i was very confused but now it all becomes more understandable.

I will play around and if i find anyting i might post some settings here you can try or make the required changes to the code first.

vabene1111 commented 2 years ago

Ok so first of all thanks for the working test setup.

What i still dont really understand is the following. Django allauth docs state:

The OpenID provider does not require any settings per se. However, a typical OpenID login page presents the user with a predefined list of OpenID providers and allows the user to input their own OpenID identity URL in case their provider is not listed by default

what is my own OpenID identity URL, where do i get one ? in authentik i find app passwords and tokens and alls kinds of things but i dont see a Open ID identity URL.

I tested google, yahoo and some others and they did not work, then i just tested https://steamcommunity.com/openid which works flawlessly without any needed configuration.

I am not sure if authentik offers what django allauth (the library we use for authentication) understands as open id.

Honestly i still dont really know how OpenID, OpenID Connect, OAuth2 and OAuth play together here.

vabene1111 commented 2 years ago

as a preparation, because i feel like we are getting somewhere, i updated the pages for openID and social auth errors to have a proper UI. I guess what needs to happen now is we need to make a list of working URL schemas for different open ID providers and make a section for this in the docs, starting with the steam link above and hopefully one for authentik as well (can imagine they dont implement this 🤔 )

kreativmonkey commented 2 years ago

I think the documentation of Django is a bit less on this point. The most time you need to spacify somthing like scope ore you add a auto discovery url in authentik: https://authentik.company/application/o/<application slug>/.well-known/openid-configuration. But in Django there is nothing to setup and no documentation what the callback url is ore somthing more....

vabene1111 commented 2 years ago

first of all its not django its the library django allauth. And this library specifically states in its documentation that no setup is required (as we have seen in the steam example). I have tried the URL you posted above but without success.

The question remains as stated in my previous comment: which providers support this kind of URL based OpenID (or whatever its called) authentication. Apart from that django allauth supports many kinds of oauth providers with the classical client secret and redirect system, but thats not what open ID is about (at least in django allauth, thus the question of the differencve between those technologies)

brulewich commented 2 years ago

I don't know django-allauth, but I might be able to help clear up your OpenID Connect questions @vabene1111. Disclaimer that this is my amateur understanding, I might be wrong.

OpenID defines a protocol for delegating authentication to an external server, whereas OAuth2 does the same for authorization. OpenID Connect (OIDC) combines the OpenID features (or rather, improves upon them) and the OAuth2 protocol; it's an identity layer built on top of OAuth2, combining authentication with authorization into one single specification. It's possible to use OAuth2 alone for authentication but it's confusing, unstandardized, and lacking in features compared to OpenID. OIDC is an attempt to remedy that. So, OIDC is a different beast than regular OpenID. OAuth2 is just a newer version of OAuth.

The flow of OIDC is the same as OAuth2, but an additional JWT (id_token) is passed to the client to authenticate users. From this additional token, an application can derive identifying user info like username, email, etc., along with details like token expiration. The OIDC specification standardizes how these "scopes" are accessed, how the provider endpoint is "discovered", among others. So the client app needs to be told by the sysadmin where to find the OIDC provider (the discovery URL that points to all the needed endpoints or, if the app didn't implement the discovery specification, the URL's to the authorization, token, and userinfo endpoints), optionally what info to request (the scopes) that be returned in the id_token, and the typical client_id and client_secret used in OAuth2. This standardization is part of its purpose: any provider (e.g. Authentik, Keycloak, Authelia) that implements the OIDC specification (the Provider role) should work with any application that also implements it (the Relaying Party role). From a cursory look at django-allauth, it seems many providers use their own implementation of OAuth2 and/or OpenID, which again is not OIDC. But it does seem like some providers (e.g. LemonLDAP::NG) are implemented using OIDC, so I'd imagine the functionality is there?

vabene1111 commented 2 years ago

very interesting writeup, thank you! This is another step in getting somewhere. I guess we would need to find out how to connect authentik to tandoor then because other than that it seems to me that open ID is in fact working as intended by django allauth and that apart from the url thingy (like steam above) allauth implements custom connectors for other oauth applications. Maybe some of them are compatible

danaelg commented 2 years ago

In my setup, when I'm on the openID connection page, I try to paste any URL authentik give me (even if I think the good one is the OpenID Configuration URL) : image

I get an 500 error... and I can't manage to find the error details in the logs. If someone can help me achieving this I think it would give us some information to take a step further.

kreativmonkey commented 2 years ago

@danaelg where do you find this setup page? in my backend under social applications i can only past the keys and give a name but i can not past any URL. For pasting the URL i use the configuration file set the SOCIALACCOUNT_PROVIDERS={'openid': {'SERVERS': {'id': 'authentik',...

danaelg commented 2 years ago

@kreativmonkey same here, don't worry. I've probably been unclear on that point. When I mentioned "The OpenID connection page" I mean the page you access by clicking on "Connect with OpenID" on Tandoor login screen (I posted a screenshot of that page in my first comment of this issue).

Hope it clarifies :smile:

danaelg commented 2 years ago

I managed to show internal server error details by setting the environment variable DEBUG to 1. It seems that the problem is on the django allauth library side.

As a workaround I set up LDAP authentication as Authentik offers a provider for that.

kreativmonkey commented 2 years ago

@danaelg did you open up a issue to the django allauth library?

danaelg commented 2 years ago

@kreativmonkey No I didn't. I've seen a request for authentik support in their repo but it doen't look like they plan to do that in the near future. Also, I don't have the knowledge to provide much details on OpenID.

As I said, the fastest way to have something working was to set up LDAP which works really well. On top of that, I needed it for an other application - I killed two birds with one stone.

vabene1111 commented 2 years ago

ok so as far as i can see tandoor does support openID for applications that actually support it, so i will close this issue

Kaaybi commented 1 year ago

For information, OIDC has been implemented in django-allauth so once it gets merged we'll likely be able to use Authentik through this 👍 https://github.com/pennersr/django-allauth/pull/3165

tomlawesome commented 1 year ago

For information, OIDC has been implemented in django-allauth so once it gets merged we'll likely be able to use Authentik through this 👍 pennersr/django-allauth#3165

Awesome to hear, this will actually solve the problem for lots of apps.

vabene1111 commented 1 year ago

this is really cool, thanks for letting us now

ikaruswill commented 1 year ago

The allauth.socialaccount.providers.openid_connect provider has just been merged and released in allauth 0.52.0 several days ago.

Pull Request Release Issue

Could we plan for an upgrade of the allauth dependency and put up a release? I'm excited to test it out!