Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support SSO (Oauth2, OIDC, or SAML) #4

Open
egwynn opened this issue Aug 17, 2024 · 18 comments
Open

Support SSO (Oauth2, OIDC, or SAML) #4

egwynn opened this issue Aug 17, 2024 · 18 comments

Comments

@egwynn
Copy link

egwynn commented Aug 17, 2024

Allow user provisioning / access to be controlled via a separate IdP.

@royaltongue
Copy link

Any updates on this? It's the last thing I need backed I can pitch using it for Christmas with my family

I was looking around and came across this: https://flask-oidc.readthedocs.io/en/latest/#

I don't have a ton of experience with python this complex overall but I thought I might as well throw some time at it when I can and see if anything works out

@icbestCA
Copy link
Owner

icbestCA commented Nov 9, 2024

Hello, I've worked on something using Keycloak and OpenID Connect. I've managed to get the information of the user who logs in. I find this very complicated, I know it's authentication so it cannot be simple. It may take a bit of time since I've never worked in that.
I have some questions;
What app are you using for the auth?
What will you use to check the user? Username or email?

Thanks for being still interested.

@royaltongue
Copy link

I've been using Authelia up until now, but I've been trying out Authentik as well. Both support OIDC so they should be compatible regardless

For the identifier, I'm thinking a username might be better? Both Authelia and Authentik require a username, but the email field is optional. Although for Authelia, I always pass 'openid' 'profile' 'email'.

There's a lot of information in Authelia's docs about OIDC scopes, which may help. That link is for OIDC 1, and there's an OAuth 2.0 section as well. And this is the OAuth 2.0 section for Authentik

icbestCA added a commit that referenced this issue Nov 9, 2024
see issue for explanation
@icbestCA
Copy link
Owner

icbestCA commented Nov 9, 2024

I've implemented the oidc in the development branch. you need to edit the .env file here my config, I use Keycloak since it simple to install, I think it should work on Authelia, can you try please? You will need to change the urls for logout and config. I used cloud-iam for keycloak. Fill the others values, I will change the variables name later.
Forgot to precise, to login you need to go to /login_oidc .
You will also need a new library: pip install authlib

KEYCLOAK_CLIENT_ID=""
KEYCLOAK_CLIENT_SECRET=""
KEYCLOAK_SERVER_METADATA_URL="https://<host>/auth/realms/<realm name>/.well-known/openid-configuration"
KEYCLOAK_LOGOUT_URL="https://<host>/realms/<realm name>/protocol/openid-connect/logout"
PRIMARY_OIDC_FIELD=email
SECONDARY_OIDC_FIELD=preferred_username
PRIMARY_DB_FIELD=email
SECONDARY_DB_FIELD=username

You can compare two value so if the user don't have an email it will use the username.
Could you try on your side with Authelia? Thanks. If you want the docker image, i will push to icbest/devgift so you can test.
Here is also my Keycloak config (I used a public IP for the flask, i don't think it work on localhost):

Root URL: http://<Flask host>
Home URL: -
Valid redirect URIs:  http://<Flask host>/auth
Valid post logout redirect URIs: http://<Flask host>
Web origins: http://<Flask host>

Here an image:
image

icbestCA added a commit that referenced this issue Nov 9, 2024
@royaltongue
Copy link

royaltongue commented Nov 9, 2024

Here is my new docker-compose.yml:

services:
##################################################################
##################################################################
##################################################################
  giftmanager:
    container_name: giftmanager
    image: icbest/devgift:latest
    volumes:
      - ${dcs}/giftmanager/app/data:/app/data
      - ${dcs}/giftmanager/backup:/backup
    hostname: giftmanager
    ports:
      - 4022:5000
    restart: unless-stopped

And my .env:

dcs=/srv/dev-disk-by-uuid-ca6f47d3-cd3d-4acc-bb5c-53a1d0eb27e5/DockerContainerStorage

KEYCLOAK_CLIENT_ID="[redacted]"
KEYCLOAK_CLIENT_SECRET="[redacted]"
KEYCLOAK_SERVER_METADATA_URL="https://authentik.[redacted].me/application/o/giftmanager/.well-known/openid-configuration"
KEYCLOAK_LOGOUT_URL="https://authentik.[redacted].me/application/o/giftmanager/end-session/"
PRIMARY_OIDC_FIELD=email
SECONDARY_OIDC_FIELD=preferred_username
PRIMARY_DB_FIELD=email
SECONDARY_DB_FIELD=username

When I try to load https://gift.[redacted].me/login_oidc, this is the error I get:

Traceback (most recent call last):
  File "/usr/local/lib/python3.11/site-packages/flask/app.py", line 1498, in __call__
    return self.wsgi_app(environ, start_response)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/flask/app.py", line 1476, in wsgi_app
    response = self.handle_exception(e)
               ^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/flask/app.py", line 1473, in wsgi_app
    response = self.full_dispatch_request()
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/flask/app.py", line 882, in full_dispatch_request
    rv = self.handle_user_exception(e)
         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/flask/app.py", line 880, in full_dispatch_request
    rv = self.dispatch_request()
         ^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/flask/app.py", line 865, in dispatch_request
    return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args)  # type: ignore[no-any-return]
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/app/app.py", line 156, in login_oidc
    return oauth.keycloak.authorize_redirect(redirect_uri, nonce=nonce, state=state)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/authlib/integrations/flask_client/apps.py", line 43, in authorize_redirect
    rv = self.create_authorization_url(redirect_uri, **kwargs)
         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/authlib/integrations/base_client/sync_app.py", line 318, in create_authorization_url
    raise RuntimeError('Missing "authorize_url" value')
RuntimeError: Missing "authorize_url" value

Opening a shell within the container and running pip instrall authlib comes back with the following:

Requirement already satisfied: authlib in /usr/local/lib/python3.11/site-packages (1.3.2)
Requirement already satisfied: cryptography in /usr/local/lib/python3.11/site-packages (from authlib) (43.0.3)
Requirement already satisfied: cffi>=1.12 in /usr/local/lib/python3.11/site-packages (from cryptography->authlib) (1.17.1)
Requirement already satisfied: pycparser in /usr/local/lib/python3.11/site-packages (from cffi>=1.12->cryptography->authlib) (2.22)
WARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv

[notice] A new release of pip is available: 24.0 -> 24.3.1
[notice] To update, run: pip install --upgrade pip

Here's a picture of the configuration panel within Authentik, if it helps:
image

Edit: By the way, if you don't want to maintain two separate repos for the stable and dev versions, Docker Hub allows you to use manifests like icbest/giftmananger:latest and/or icbest/giftmanager:stable (people usually make these the same version), and then icbest/giftmanager:dev or icbest/giftmananger:beta for these test images

@icbestCA
Copy link
Owner

icbestCA commented Nov 9, 2024

Hello, just installed Authentik and tested myself. Everything worked correctly. Could you just make sure your user have the right logins info (emails are similar or usernames) and you also may restart the container after updating the .env in /setup.
If still dont work, you will need to give me more infos because i dont see the problems right now.
Thanks

@royaltongue
Copy link

Unfortunately I've still not been able to get it working. So far I've tried:

  • Deleting and recreating the container
  • Deleting the container, deleting the folder its data was in, then recreating the container, then going to /login_oidc
  • Again with a fresh container and folder, made a new account with the email I use for Authentik, then went to /login_oidc
  • Changed https://authentik.[redacted].me/application/o/giftmanager/.well-known/openid-configuration to https://authentik.[redacted].me/application/o/giftmanager/

Everything still results in the same error. Can you provide the environment variables you're using for your Authentik setup?

@icbestCA
Copy link
Owner

OK, here is the detailed steps I've done:

  1. Installed Authentik on a new Linode vps using this guide

  2. Setup the admin account

  3. Created a new provider called it giftmanager using OAuth2/OpenID Provider and keeping all default parameters.

  4. Ran the container of giftmanager on a machine with public exposed ip using the command: docker run -p 5055:5000 -d -v /root/data:/app/data icbest/devgift
    image

  5. Created an account with this email ([email protected]) and this username (icbest). Deleted default profiles.

  6. Restarted the gift app

  7. Created an account, with the same infos, on authentik. Username = icbest Email = [email protected]

  8. Added a new app in authentik image

  9. Gone to /setup in the giftmanager and added the values to .env file here they are: KEYCLOAK_CLIENT_ID='bcG3BBvGI2upvILrdK5hnvUMCIUmx2Y04cMTck7M'
    KEYCLOAK_CLIENT_SECRET='QrFVKTrYFf4PHPbfyKeUZVsbSiZWxaDCFw1TGmNI8iMLfEuK5mgiLgpBevvz6OnHa58Fc3aim3c5INMPT0NjDwxzz82mV6bfKpEb3Fteh4EFqRzKNGqf8AK0P9nhcVg6'
    KEYCLOAK_SERVER_METADATA_URL='http://ip:9000/application/o/giftmanager/.well-known/openid-configuration'
    KEYCLOAK_LOGOUT_URL='http://ip:9000/application/o/giftmanager/'
    PRIMARY_OIDC_FIELD='email'
    SECONDARY_OIDC_FIELD='preferred_username'
    PRIMARY_DB_FIELD='email'
    SECONDARY_DB_FIELD='username'

  10. I restarted the gift container after updating the values

  11. Logout of Authentik and gift app

  12. Go to /login_oidc

  13. logged on Authentik using Icbest profile

  14. Returned to /login_oidc get this window and click continue: image

  15. Then successfully logged into the gift app.

The Linode is deleted, don't worry for personal infos.
All worked while doing these steps in order. I even tested with a different username but right email, and it still worked.
Hope it works.

@royaltongue
Copy link

royaltongue commented Nov 10, 2024

Oh... I didn't go into that /setup page at all. Would it make more sense to make these environment variables that can be set within the docker.compose.yml?

Anyway, once I set everything within that page, it srtated to work, sort of. I set the redirect URL: to https://gift.[redacted].me/auth at first, but Authentik gave me an error about a mismatch. I set the redirect to * for now and it works great otherwise. What do you have your redirect set as?

@royaltongue
Copy link

Also it would be really helpful to (maybe as an option) automatically create new GiftManager accounts if a user successfully authenticates with OIDC but does not have a user registered with GiftManager yet

@icbestCA
Copy link
Owner

I'm almost done writing everything for release. I just need to know how you want to set the environment variables in the Docker Compose. I'm not sure what the best way to do this is; any recommendations or tips are welcome.
Thanks

@royaltongue
Copy link

royaltongue commented Nov 13, 2024

Within the Dockerfile, you can set up environment variables with defaults like this:

ENV TA_CACHE=/cache \
    TA_MEDIA_FOLDER=/youtube \
    TARGET_FOLDER=/youtube-processed \
    APPRISE_TRIGGER_PORT=8001 \
    TA_HELPER_SCRIPT=/app/ta-helper.py \
#
    LOGLEVEL=INFO \
    TA_SERVER=http://192.168.1.5:8000 \
    TA_TOKEN=c0ff142e1c336e9f43be560b5c942d61e7e7c7fb \
    NOTIFICATIONS_ENABLED=true \
    [email protected] \
    [email protected],[email protected] \
    GENERATE_NFO=true \
    APPRISE_LINK=mailto://<username>:<password>@gmail.com \
    QUICK=true \
    CLEANUP_DELETED_VIDEOS=false \
    PUID=1049 \
    GUID=1049

That # in the middle was just to separate required values from optional values to make it easier for me to see. It doesn't have a purpose

And inside the docker-compose.yml it looks like this:

services:
  giftmanager:
    container_name: giftmanager
    image: icbest/devgift:latest
    environment:
      - SOME_VAR=some-value
      - someothervar=someothervalue
    volumes:
      - ./data:/app/data
      - ./backup:/backup
    hostname: giftmanager
    ports:
      - 5000:5000
    restart: unless-stopped

icbestCA added a commit that referenced this issue Nov 14, 2024
Able to connect via an OpenID connect provider. Admin can choose scope to compare and allow auto registration.
@icbestCA
Copy link
Owner

Just pushed the changes, it also allow auto-registration. Sadly I didn't successfully implemented a way to put the values in the docker-compose. You will have to either modify the .env via docker exec or in /setup . I will write the documentation related to OIDC soon. Thanks for the help.

@royaltongue
Copy link

Thank you!

@vfedetz
Copy link

vfedetz commented Nov 22, 2024

Tried configuring Google as my OIDC provider, but am getting this error when attempting to load https://gift.[redacted].net/login_oidc

Traceback (most recent call last):

  File "/usr/local/lib/python3.11/site-packages/flask/app.py", line 1536, in __call__

    return self.wsgi_app(environ, start_response)

           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

  File "/usr/local/lib/python3.11/site-packages/flask/app.py", line 1514, in wsgi_app

    response = self.handle_exception(e)

               ^^^^^^^^^^^^^^^^^^^^^^^^

  File "/usr/local/lib/python3.11/site-packages/flask/app.py", line 1511, in wsgi_app

    response = self.full_dispatch_request()

               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^

  File "/usr/local/lib/python3.11/site-packages/flask/app.py", line 919, in full_dispatch_request

    rv = self.handle_user_exception(e)

         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

  File "/usr/local/lib/python3.11/site-packages/flask/app.py", line 917, in full_dispatch_request

    rv = self.dispatch_request()

         ^^^^^^^^^^^^^^^^^^^^^^^

  File "/usr/local/lib/python3.11/site-packages/flask/app.py", line 902, in dispatch_request

    return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args)  # type: ignore[no-any-return]

           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

  File "/app/app.py", line 152, in login_oidc

    nonce = secrets.token_urlsafe(16)

            ^^^^^^^^^^^^^^^^^^^^^^^^^

NameError: name 'secrets' is not defined

Here is my .env file:

MAILJET_API_KEY=''
MAILJET_API_SECRET=''
SECRET_KEY='changethis'
SYSTEM_EMAIL=''
DELETE_DAYS='30'
OIDC_CLIENT_ID='[redacted]'
OIDC_CLIENT_SECRET='[redacted]'
OIDC_SERVER_METADATA_URL='https://accounts.google.com/.well-known/openid-configuration'
OIDC_LOGOUT_URL='https://accounts.google.com/Logout'
PRIMARY_OIDC_FIELD='email'
SECONDARY_OIDC_FIELD='preferred_username'
PRIMARY_DB_FIELD='email'
SECONDARY_DB_FIELD='username'
ENABLE_AUTO_REGISTRATION='true'

And docker compose file:

services:
  giftmanager:
    image: icbest/giftmanager:latest
    container_name: giftmanager
    ports:
      - "5001:5000"
    volumes:
      - /home/vfedetz/docker/giftmanager:/app/data/
    restart: unless-stopped

@icbestCA
Copy link
Owner

Yeah, good. Now corrected; see v.1.0.2
Docker latest and 1.0.2 now available.
Thanks

@icbestCA
Copy link
Owner

It seem that there is a small bug with the auto registration feature. I'm on it. I still don't know the cause.

@icbestCA
Copy link
Owner

The issue is now fixed.
It was only the docker json url that wasn't referenced correctly in the setup_profile route.
with open(app.config['USERS_FILE'], "w") as file: json.dump(users, file, indent=4)

See v1.0.3
Image upload to docker hub.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants