Skip to content

Instantly share code, notes, and snippets.

@thomasdarimont
Last active May 11, 2024 09:57
Show Gist options
  • Save thomasdarimont/145dc9aa857b831ff2eff221b79d179a to your computer and use it in GitHub Desktop.
Save thomasdarimont/145dc9aa857b831ff2eff221b79d179a to your computer and use it in GitHub Desktop.
Simple python example using flask, flask_oidc and keycloak
import json
import logging
from flask import Flask, g
from flask_oidc import OpenIDConnect
import requests
logging.basicConfig(level=logging.DEBUG)
app = Flask(__name__)
app.config.update({
'SECRET_KEY': 'SomethingNotEntirelySecret',
'TESTING': True,
'DEBUG': True,
'OIDC_CLIENT_SECRETS': 'client_secrets.json',
'OIDC_ID_TOKEN_COOKIE_SECURE': False,
'OIDC_REQUIRE_VERIFIED_EMAIL': False,
'OIDC_USER_INFO_ENABLED': True,
'OIDC_OPENID_REALM': 'flask-demo',
'OIDC_SCOPES': ['openid', 'email', 'profile'],
'OIDC_INTROSPECTION_AUTH_METHOD': 'client_secret_post'
})
oidc = OpenIDConnect(app)
@app.route('/')
def hello_world():
if oidc.user_loggedin:
return ('Hello, %s, <a href="/private">See private</a> '
'<a href="/logout">Log out</a>') % \
oidc.user_getfield('preferred_username')
else:
return 'Welcome anonymous, <a href="/private">Log in</a>'
@app.route('/private')
@oidc.require_login
def hello_me():
"""Example for protected endpoint that extracts private information from the OpenID Connect id_token.
Uses the accompanied access_token to access a backend service.
"""
info = oidc.user_getinfo(['preferred_username', 'email', 'sub'])
username = info.get('preferred_username')
email = info.get('email')
user_id = info.get('sub')
if user_id in oidc.credentials_store:
try:
from oauth2client.client import OAuth2Credentials
access_token = OAuth2Credentials.from_json(oidc.credentials_store[user_id]).access_token
print 'access_token=<%s>' % access_token
headers = {'Authorization': 'Bearer %s' % (access_token)}
# YOLO
greeting = requests.get('http://localhost:8080/greeting', headers=headers).text
except:
print "Could not access greeting-service"
greeting = "Hello %s" % username
return ("""%s your email is %s and your user_id is %s!
<ul>
<li><a href="/">Home</a></li>
<li><a href="//localhost:8081/auth/realms/pysaar/account?referrer=flask-app&referrer_uri=http://localhost:5000/private&">Account</a></li>
</ul>""" %
(greeting, email, user_id))
@app.route('/api', methods=['POST'])
@oidc.accept_token(require_token=True, scopes_required=['openid'])
def hello_api():
"""OAuth 2.0 protected API endpoint accessible via AccessToken"""
return json.dumps({'hello': 'Welcome %s' % g.oidc_token_info['sub']})
@app.route('/logout')
def logout():
"""Performs local logout by removing the session cookie."""
oidc.logout()
return 'Hi, you have been logged out! <a href="/">Return</a>'
if __name__ == '__main__':
app.run()
{
"web": {
"issuer": "http://localhost:8081/auth/realms/pysaar",
"auth_uri": "http://localhost:8081/auth/realms/pysaar/protocol/openid-connect/auth",
"client_id": "flask-app",
"client_secret": "a41060dd-b5a8-472e-a91f-6a3ab0e04714",
"redirect_uris": [
"http://localhost:5000/*"
],
"userinfo_uri": "http://localhost:8081/auth/realms/pysaar/protocol/openid-connect/userinfo",
"token_uri": "http://localhost:8081/auth/realms/pysaar/protocol/openid-connect/token",
"token_introspection_uri": "http://localhost:8081/auth/realms/pysaar/protocol/openid-connect/token/introspect"
}
}
@otto2704
Copy link

Hi,
thanks for this example. It works so far. But now I have to add some dash stuff.
Dash needs a function for layout. As soon as I am using this, the oidc stuff does not work anymore (and oidc is not recognised within the layout function). Does anyone maybe have a working example for flask plus dash and could post it?

@robertoneto-senior
Copy link

robertoneto-senior commented Feb 8, 2024

For logout I was only able to do it with:

@app.route('/signout')
def logout():
    id_token = session.get('oidc_auth_token').get('id_token')
    return redirect(
        "https://my-key-cloak/realms/my-realm/protocol/openid-connect/logout?id_token_hint=%s&post_logout_redirect_uri=%s" % (id_token, urllib.parse.quote("http://localhost/logout", safe='')))

The signout process will logout on keycloak and redirect to /logout to discard cookies.

And I changed the link on main page to point to /signout instead of /logout.

This "fix" was based on keycloak 18 upgrade docs:

@alex27riva
Copy link

Hi everyone, does this code still works?
It seems that the endpoints have changed from http://localhost:8081/auth/realms/pysaar to http://localhost:8081/realms/pysaar

I tried to run the app but I get this error message:
immagine

Maybe because this method is not resolved:
immagine

@LUCIFERsDen26
Copy link

LUCIFERsDen26 commented Mar 12, 2024

Hello! @alex27riva

I got it working! 🎉 GitHub Gist Link

docker run -p 8080:8080 -e KEYCLOAK_ADMIN=admin -e KEYCLOAK_ADMIN_PASSWORD=admin quay.io/keycloak/keycloak:24.0.1 start-dev

Then I built the realm and client, and modified the code according to the latest release of flask-oidc (1.2.0) (Sep 28, 2017).

Thank you!

If you've discovered a new way to deal with Flask Keycloak, please let me know too!

@alex27riva
Copy link

Hello! @alex27riva

I got it working! 🎉 GitHub Gist Link

docker run -p 8080:8080 -e KEYCLOAK_ADMIN=admin -e KEYCLOAK_ADMIN_PASSWORD=admin quay.io/keycloak/keycloak:24.0.1 start-dev

Then I built the realm and client, and modified the code according to the latest release of flask-oidc (1.2.0) (Sep 28, 2017).

Thank you!

If you've discovered a new way to deal with Flask Keycloak, please let me know too!

Hi @LUCIFERsDen26 , thank your for your reply.
I tried your code, but I'm getting the same error as before.
immagine
Are these client setting correct?

@LUCIFERsDen26
Copy link

Hello! @alex27riva
I got it working! 🎉 GitHub Gist Link

docker run -p 8080:8080 -e KEYCLOAK_ADMIN=admin -e KEYCLOAK_ADMIN_PASSWORD=admin quay.io/keycloak/keycloak:24.0.1 start-dev

Then I built the realm and client, and modified the code according to the latest release of flask-oidc (1.2.0) (Sep 28, 2017).
Thank you!
If you've discovered a new way to deal with Flask Keycloak, please let me know too!

Hi @LUCIFERsDen26 , thank your for your reply. I tried your code, but I'm getting the same error as before. immagine Are these client setting correct?

Hey!
thanks for reaching out!
the adress should be same as you python flask app is running on like shown in image!

BTW it has username : lucifer
password: test (i guess, i dont remenber, i change it from admin account)

image

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