Our OpenID Connect library
Companies often manage a collection of internal services/tools/platforms; hosted in various ways, managed in various ways, and relevant to this blogpost: each requiring a different set of user credentials. While most are familiar with a password manager, it would be nice to only have to deal with one account. This prevents from having to create and distribute a list of login accounts during the on-boarding process of new employees, as well as ease up termination by revoking access in one sweep.
An identity provider (IdP) is a service that authenticates users, generally via OpenID Connect - a flavor of OAuth2. Below is a list of software that are IdP's:
Users authenticate against an IdP rather than against individual applications. This means that applications don't have to deal with login forms, authentication, or storing users. They would all use a single IdP, effectively outsourcing their authentication and authorization business logic.
Once logged-in to an IdP, users don't have to log in again to access a different application (single-sign-on). Centralizing user accounts in a single system means that properties related to authentication only need to be defined in one place, as opposed to individual applications having to re-invent the wheel, such as:
This saves time (and mistakes) as the above is the responsibility of a single system that is designed for it.
Not all applications, tools, and services support login via an IdP, and persuading software authors to implement OpenID Connect comes with challenges. The specification is extensive, and using it properly requires research.
When writing web-applications in Python, we generally use one of the following frameworks:
(As you may notice; both are asyncio frameworks)
In order to provide login functionality to our Quart applications, we've created an extension called quart-keycloak. This extension puts an abstract layer over the complicated parts of OpenID Connect and makes a fair assumption that the user (developer) wants basic OpenID Connect features, mainly: login, and logout.
This allows us quickly develop web-applications where users can authenticate themselves, without the need for us to create login functionality for each project.
A minimal example of an Quart application that implements OIDC:
from quart import Quart, url_for, jsonify, session, redirect
from quart_session import Session
from quart_keycloak import Keycloak, KeycloakAuthToken, KeycloakLogoutRequest
app = Quart(__name__)
app.secret_key = 'changeme'
app.config['SESSION_TYPE'] = 'redis'
Session(app)
openid_keycloak_config = {
"client_id": "",
"client_secret": "",
"configuration": "https://example.com/realms/master/.well-known/openid-configuration"
}
keycloak = Keycloak(app, **openid_keycloak_config)
@app.route("/")
async def root():
# redirect the user after logout
logout_url = url_for('logout', _external=True)
# the login URL
login_url_keycloak = url_for(keycloak.endpoint_name_login)
# the logout URL, `redirect_uri` is required. `state` is optional.
logout_url_keycloak = url_for(keycloak.endpoint_name_logout, redirect_uri=logout_url, state='bla')
return f"""
<b>token:</b> {session.get('auth_token')}<br><hr>
Login via keycloak: <a href="{login_url_keycloak}">Login via Keycloak</a><br>
Logout via keycloak: <a href="{logout_url_keycloak}">Logout via Keycloak</a>
"""
@keycloak.after_login() # user was redirected back from Keycloak
async def handle_user_login(auth_token: KeycloakAuthToken):
# user = await keycloak.user_info(auth_token.access_token) # optionally call the userinfo endpoint for more info
# set session
session['auth_token'] = auth_token
return redirect(url_for('root'))
@app.route("/logout") # route that clears the session
async def logout():
session.clear()
return redirect(url_for('root'))
app.run("localhost", port=2700, debug=True, use_reloader=False)
Due to the "enterprise" nature of this technology you may be under the impression OpenID Connect is only applicable to large companies with exotic requirements, but we think otherwise. While complex, it can support any type of ecosystem that requires login functionality - even if it concerns managing some sort of informal community. By leveraging OIDC, you'll be prepared for future growth and save yourself growing pains when introducing new services to your ecosystem, as those services can immediately leverage your existing user management infrastructure.
To conclude, we have experience with: