Integrating Authentiction into your Web Applications#
Freva supports OAuth2-based authentication using the Authorization Code Flow.
You can use the authentication endpoints of Freva’s RestAPI to secure your web applications with OpenID Connect (OIDC) authentication flow and creating OAuth2 tokens.
Authentication Flow#
The standard OIDC Authorization Code flow involves three steps:
1. Redirect to Login#
Redirect the user to the login endpoint with the redirect_uri parameter.
GET /api/freva-nextgen/auth/v2/login HTTP/1.1
host: www.freva.dkrz.de
{
redirect_uri=http://localhost:8050/callback
}
3. Retrieve User Info (optional):#
Use the gnerated access token to fetch the user’s identity:
GET /api/freva-nextgen/auth/v2/userinfo HTTP/1.1
host: www.freva.dkrz.de
Authorization: Bearer access_token
Alternatively, you can use the /systemuser
endpoint to retrieve more detailed i
information about the authenticated user. This endpoint is only accessible
to primary (non-guest) users, making it useful for enforcing access
restrictions or authorization in your application.
By calling /systemuser
with a valid access token, you can reliably verify
whether the current user is a full (primary) account holder.
GET /api/freva-nextgen/auth/v2/systemuser HTTP/1.1
host: www.freva.dkrz.de
Authorization: Bearer access_token
Dash Example#
The following Plotly Dash app demonstrates how to integrate this authentication flow using Flask sessions to store tokens. It uses requests for API interaction and displays a simple authenticated Plotly graph.
1import os
2from typing import Any, Dict, Optional
3
4import dash
5import plotly.express as px
6import requests
7from dash import Dash, Input, Output, dcc, html
8from flask import Flask, Response, redirect, request, session
9
10# =============================
11# CONFIGURATION
12# =============================
13
14FREVA_API_BASE: str = "https://freva.dkrz.de/api/freva-nextgen/auth/v2"
15REDIRECT_URI: str = "http://localhost:8050/callback"
16LOGIN_ENDPOINT: str = f"{FREVA_API_BASE}/login"
17TOKEN_ENDPOINT: str = f"{FREVA_API_BASE}/token"
18USERINFO_ENDPOINT: str = f"{FREVA_API_BASE}/userinfo"
19
20# Initialize Flask server for Dash backend
21server: Flask = Flask(__name__)
22
23# ⚠️ SECURITY WARNING:
24# This key signs the session cookie. In production, use a secure env var:
25# e.g., server.secret_key = os.environ["FLASK_SECRET_KEY"]
26server.secret_key = os.urandom(24)
27
28# =============================
29# DASH APP INITIALIZATION
30# =============================
31
32app: Dash = dash.Dash(
33 __name__, server=server, use_pages=False, suppress_callback_exceptions=True
34)
35app.title = "Freva OIDC Dash Example"
36
37# =============================
38# FLASK AUTH ROUTES
39# =============================
40
41
42@server.route("/login")
43def login() -> Response:
44 """Redirects user to Freva's OIDC login page."""
45 return redirect(f"{LOGIN_ENDPOINT}?redirect_uri={REDIRECT_URI}")
46
47
48@server.route("/callback")
49def callback() -> Response:
50 """Handles OIDC redirect, exchanges code for token, and stores in session."""
51 code: Optional[str] = request.args.get("code")
52 state: Optional[str] = request.args.get("state")
53
54 if not code or not state:
55 return Response("Missing code or state", status=400)
56
57 try:
58 # Exchange authorization code for token
59 token_res = requests.post(
60 TOKEN_ENDPOINT,
61 data={
62 "code": code,
63 "redirect_uri": REDIRECT_URI,
64 "grant_type": "authorization_code",
65 },
66 )
67 token_res.raise_for_status()
68 tokens: Dict[str, Any] = token_res.json()
69 session["access_token"] = tokens["access_token"]
70
71 # Retrieve user info
72 userinfo_res = requests.get(
73 USERINFO_ENDPOINT,
74 headers={"Authorization": f"Bearer {tokens['access_token']}"},
75 )
76 userinfo_res.raise_for_status()
77 session["userinfo"] = userinfo_res.json()
78
79 except requests.RequestException as e:
80 return Response(f"Login failed: {str(e)}", status=500)
81
82 return redirect("/")
83
84
85@server.route("/logout")
86def logout() -> Response:
87 """Clears the session and logs user out."""
88 session.clear()
89 return redirect("/")
90
91
92# =============================
93# DASH LAYOUT AND CALLBACK
94# =============================
95
96app.layout = html.Div([dcc.Location(id="url", refresh=True), html.Div(id="main")])
97
98
99@app.callback(Output("main", "children"), Input("url", "pathname"))
100def render_content(_: str) -> Any:
101 """Main page content: authenticated view or redirect to login."""
102 if "userinfo" not in session:
103 return dcc.Location(href="/login", id="redirect")
104
105 user: str = session["userinfo"].get("username", "Unknown")
106 access_token: str = session["access_token"]
107
108 fig = px.scatter(x=[1, 2, 3], y=[3, 1, 6], title="Freva Demo Scatter")
109
110 return html.Div(
111 [
112 html.H2(f"Welcome, {user}"),
113 html.P("Your OAuth2 token (access_token) is:"),
114 html.Pre(
115 access_token,
116 style={"whiteSpace": "pre-wrap", "wordBreak": "break-all"},
117 ),
118 dcc.Graph(figure=fig),
119 html.Br(),
120 html.A("Logout", href="/logout"),
121 ]
122 )
123
124
125# =============================
126# LOCAL ENTRY POINT (FOR DEBUG)
127# =============================
128
129if __name__ == "__main__":
130 # Use `gunicorn main:server` for production deployment
131 app.run(debug=True)
You can download the example file here: dash_auth_example.py
.
Security Notes
- Redirect URI: Must match the URI registered with the identity provider.
Contact your freindly freva admins for advice.
Session Secret: Set
server.secret_key
using a secure environment variable in production.HTTPS: Always use HTTPS in production to protect token integrity.
Use a WSGI-compatible server like Gunicorn for deployment:
gunicorn dash_auth_example:server --bind 0.0.0.0:8050