Skip to content

Commit d831c1b

Browse files
committed
functions for accessing user claims from OIDC tokens + documentation
- Updated SQLPage authentication component documentation for clarity on usage and options. - Removed deprecated login and redirect handler scripts to streamline the SSO implementation. - Enhanced logout functionality to properly clear session cookies and redirect users. - Improved request handling to include OIDC claims in the request context for better user information retrieval.
1 parent 7f20cd2 commit d831c1b

File tree

13 files changed

+538
-150
lines changed

13 files changed

+538
-150
lines changed

examples/official-site/sqlpage/migrations/07_authentication.sql

Lines changed: 37 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,41 @@
22
INSERT INTO component (name, description, icon, introduced_in_version)
33
VALUES (
44
'authentication',
5-
'An advanced component that can be used to create pages with password-restricted access.
6-
When used, this component has to be at the top of your page, because once the page has begun being sent to the browser, it is too late to restrict access to it.
7-
The authentication component checks if the user has sent the correct password, and if not, redirects them to the URL specified in the link parameter.
8-
If you don''t want to re-check the password on every page (which is an expensive operation),
9-
you can check the password only once and store a session token in your database.
10-
You can use the cookie component to set the session token cookie in the client browser,
11-
and then check whether the token matches what you stored in subsequent pages.',
5+
'
6+
Create pages with password-restricted access.
7+
8+
9+
When you want to add user authentication to your SQLPage application,
10+
you have two main options:
11+
12+
1. The `authentication` component:
13+
- lets you manage usernames and passwords yourself
14+
- does not require any external service
15+
- gives you fine-grained control over
16+
- which pages and actions are protected
17+
- the look of the login form
18+
- the duration of the session
19+
- the permissions of each user
20+
2. [**Single sign-on**](/sso)
21+
- lets users log in with their existing accounts (like Google, Microsoft, or your organization''s own identity provider)
22+
- requires setting up an external service (Google, Microsoft, etc.)
23+
- frees you from implementing a lot of features like password reset, account creation, user management, etc.
24+
25+
This page describes the first option.
26+
27+
When used, this component has to be at the top of your page,
28+
because once the page has begun being sent to the browser,
29+
it is too late to restrict access to it.
30+
31+
The authentication component checks if the user has sent the correct password,
32+
and if not, redirects them to the URL specified in the link parameter.
33+
34+
If you don''t want to re-check the password on every page (which is an expensive operation),
35+
you can check the password only once and store a session token in your database
36+
(see the session example below).
37+
38+
You can use the [cookie component](?component=cookie) to set the session token cookie in the client browser,
39+
and then check whether the token matches what you stored in subsequent pages.',
1240
'lock',
1341
'0.7.2'
1442
);
@@ -158,9 +186,6 @@ RETURNING
158186
### Single sign-on with OIDC (OpenID Connect)
159187
160188
If you don''t want to manage your own user database,
161-
you can use OpenID Connect and OAuth2 to authenticate users.
162-
This allows users to log in with their Google, Facebook, or internal company account.
163-
164-
You will find an example of how to do this in the
165-
[Single sign-on with OIDC](https://github.com/sqlpage/SQLPage/tree/main/examples/single%20sign%20on).
189+
you can [use OpenID Connect and OAuth2](/sso) to authenticate users.
190+
This allows users to log in with their Google, Microsoft, or internal company account.
166191
');
Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
INSERT INTO
2+
sqlpage_functions (
3+
"name",
4+
"introduced_in_version",
5+
"icon",
6+
"description_md"
7+
)
8+
VALUES
9+
(
10+
'user_info_token',
11+
'0.35.0',
12+
'key',
13+
'# Accessing information about the current user, when logged in with SSO
14+
15+
This function can be used only when you have [configured Single Sign-On with an OIDC provider](/sso).
16+
17+
## The ID Token
18+
19+
When a user logs in through OIDC, your application receives an [identity token](https://openid.net/specs/openid-connect-core-1_0.html#IDToken) from the identity provider.
20+
This token contains information about the user, such as their name and email address.
21+
The `sqlpage.user_info_token()` function lets you access the entire contents of the ID token, as a JSON object.
22+
You can then use [your database''s JSON functions](/blog.sql?post=JSON+in+SQL%3A+A+Comprehensive+Guide) to process that JSON.
23+
24+
If you need to access a specific claim, it is easier and more performant to use the
25+
[`sqlpage.user_info()`](?function=user_info) function instead.
26+
27+
### Example: Displaying User Information
28+
29+
```sql
30+
select ''list'' as component;
31+
select key as title, value as description
32+
from json_each(sqlpage.user_info_token());
33+
```
34+
35+
This sqlite-specific example will show all the information available about the current user, such as:
36+
- `sub`: A unique identifier for the user
37+
- `name`: The user''s full name
38+
- `email`: The user''s email address
39+
- `picture`: A URL to the user''s profile picture
40+
41+
### Security Notes
42+
43+
- The ID token is automatically verified by SQLPage to ensure it hasn''t been tampered with.
44+
- The token is only available to authenticated users: if no user is logged in or sso is not configured, this function returns NULL
45+
- If some information is not available in the token, you have to configure it on your OIDC provider, SQLPage can''t do anything about it.
46+
- The token is stored in a signed http-only cookie named `sqlpage_auth`. You can use [the cookie component](/component.sql?component=cookie) to delete it, and the user will be redirected to the login page on the next page load.
47+
'
48+
);
49+
50+
INSERT INTO
51+
sqlpage_functions (
52+
"name",
53+
"introduced_in_version",
54+
"icon",
55+
"description_md"
56+
)
57+
VALUES
58+
(
59+
'user_info',
60+
'0.34.0',
61+
'user',
62+
'# Accessing Specific User Information
63+
64+
The `sqlpage.user_info` function is a convenient way to access specific pieces of information about the currently logged-in user.
65+
When you [configure Single Sign-On](/sso), your OIDC provider will issue an [ID token](https://openid.net/specs/openid-connect-core-1_0.html#IDToken) for the user,
66+
which contains *claims*, with information about the user.
67+
68+
Calling `sqlpage.user_info(claim_name)` lets you access these claims directly from SQL.
69+
70+
## How to Use
71+
72+
The function takes one parameter: the name of the *claim* (the piece of information you want to retrieve).
73+
74+
For example, to display a personalized welcome message, with the user''s name, you can use:
75+
76+
```sql
77+
select ''text'' as component;
78+
select ''Welcome, '' || sqlpage.user_info(''name'') || ''!'' as title;
79+
```
80+
81+
## Available Information
82+
83+
The exact information available depends on your identity provider (the service you chose to authenticate with),
84+
its configuration, and the scopes you requested.
85+
Use [`sqlpage.user_info_token()`](?function=user_info_token) to see all the information available in the ID token of the current user.
86+
87+
Here are some commonly available fields:
88+
89+
### Basic Information
90+
- `name`: The user''s full name (usually first and last name separated by a space)
91+
- `email`: The user''s email address (*warning*: there is no guarantee that the user currently controls this email address. Use the `sub` claim for database references instead.)
92+
- `picture`: URL to the user''s profile picture
93+
94+
### User Identifiers
95+
- `sub`: A unique identifier for the user (use this to uniquely identify the user in your database)
96+
- `preferred_username`: The username the user prefers to use
97+
98+
### Name Components
99+
- `given_name`: The user''s first name
100+
- `family_name`: The user''s last name
101+
102+
## Examples
103+
104+
### Personalized Welcome Message
105+
```sql
106+
select ''text'' as component,
107+
''Welcome back, **'' || sqlpage.user_info(''given_name'') || ''**!'' as contents_md;
108+
```
109+
110+
### User Profile Card
111+
```sql
112+
select ''card'' as component;
113+
select
114+
sqlpage.user_info(''name'') as title,
115+
sqlpage.user_info(''email'') as description,
116+
sqlpage.user_info(''picture'') as image;
117+
```
118+
119+
### Conditional Content Based on custom claims
120+
121+
Some identity providers let you add custom claims to the ID token.
122+
This lets you customize the behavior of your application based on arbitrary user attributes,
123+
such as the user''s role.
124+
125+
```sql
126+
-- show everything to admins, only public items to others
127+
select ''list'' as component;
128+
select title from my_items
129+
where is_public or sqlpage.user_info(''role'') = ''admin''
130+
```
131+
132+
## Security Best Practices
133+
134+
> ⚠️ **Important**: Always use the `sub` claim to identify users in your database, not their email address.
135+
> The `sub` claim is guaranteed to be unique and stable for each user, while email addresses can change.
136+
> In most providers, receiving an id token with a given email does not guarantee that the user currently controls that email.
137+
138+
```sql
139+
-- Store the user''s ID in your database
140+
insert into user_preferences (user_id, theme)
141+
values (sqlpage.user_info(''sub''), ''dark'');
142+
```
143+
144+
## Troubleshooting
145+
146+
If you''re not getting the information you expect:
147+
148+
1. Check that OIDC is properly configured in your `sqlpage.json`
149+
2. Verify that you requested the right scopes in your OIDC configuration
150+
3. Try using `sqlpage.user_info_token()` to see all available information
151+
4. Check your OIDC provider''s documentation for the exact claim names they use
152+
153+
Remember: If the user is not logged in or the requested information is not available, this function returns NULL.
154+
'
155+
);
156+
157+
INSERT INTO
158+
sqlpage_function_parameters (
159+
"function",
160+
"index",
161+
"name",
162+
"description_md",
163+
"type"
164+
)
165+
VALUES
166+
(
167+
'user_info',
168+
1,
169+
'claim',
170+
'The name of the user information to retrieve. Common values include ''name'', ''email'', ''picture'', ''sub'', ''preferred_username'', ''given_name'', and ''family_name''. The exact values available depend on your OIDC provider and configuration.',
171+
'TEXT'
172+
);
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
select 'http_header' as component,
2+
'public, max-age=600, stale-while-revalidate=3600, stale-if-error=86400' as "Cache-Control",
3+
'<https://sql-page.com/sso>; rel="canonical"' as "Link";
4+
5+
select 'dynamic' as component, properties FROM example WHERE component = 'shell' LIMIT 1;
6+
7+
select 'text' as component, sqlpage.read_file_as_text('sso/single_sign_on.md') as contents_md, true as article;
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
# Setting Up Single Sign-On in SQLPage
2+
3+
4+
When you want to add user authentication to your SQLPage application, you have two main options:
5+
6+
1. The [authentication component](/component.sql?component=authentication):
7+
A simple username/password system, that you have to manage yourself.
8+
2. **OpenID Connect (OIDC)**:
9+
A single sign-on system that lets users log in with their existing accounts (like Google, Microsoft, or your organization's own identity provider).
10+
11+
This guide will help you set up single sign-on using OpenID connect with SQLPage quickly.
12+
13+
## Essential Terms
14+
15+
- **OIDC** ([OpenID Connect](https://openid.net/developers/how-connect-works/)): The protocol that enables secure login with existing accounts. While it adds some complexity, it's an industry standard that ensures your users' data stays safe.
16+
- **Issuer** (or identity provider): The service that verifies your users' identity (like Google or Microsoft)
17+
- **Identity Token**: A secure message from the issuer containing user information. It is stored as a cookie on the user's computer, and sent with every request after login. SQLPage will redirect all requests that do not contain a valid token to the identity provider's login page.
18+
- **Claim**: A piece of information contained in the token about the user (like their name or email)
19+
20+
## Quick Setup Guide
21+
22+
### Choose an OIDC Provider
23+
24+
Here are the setup guides for
25+
[Google](https://developers.google.com/identity/openid-connect/openid-connect),
26+
[Microsoft Entra ID](https://learn.microsoft.com/en-us/entra/identity-platform/quickstart-register-app),
27+
[GitHub](https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/authorizing-oauth-apps),
28+
and [Keycloak](https://www.keycloak.org/getting-started/getting-started-docker) (self-hosted).
29+
30+
### Register Your Application
31+
32+
1. Go to your chosen provider's developer console
33+
2. Create a new application
34+
3. Set the redirect URI to `http://localhost:8080/sqlpage/oidc_callback`. (We will change that later when you deploy your site to a hosting provider such as [datapage](https://beta.datapage.app/)).
35+
4. Note down the client ID and client secret
36+
37+
### Configure SQLPage
38+
39+
Create or edit `sqlpage/sqlpage.json` to add the following configuration keys:
40+
41+
```json
42+
{
43+
"oidc_issuer_url": "https://accounts.google.com",
44+
"oidc_client_id": "your-client-id",
45+
"oidc_client_secret": "your-client-secret",
46+
"host": "localhost:8080"
47+
}
48+
```
49+
50+
#### Provider-specific settings
51+
- Google: `https://accounts.google.com`
52+
- Microsoft: `https://login.microsoftonline.com/{tenant}/v2.0`. [Find your value of `{tenant}`](https://learn.microsoft.com/en-us/entra/identity-platform/quickstart-create-new-tenant).
53+
- GitHub: `https://github.com`
54+
- Keycloak: Use [your realm's base url](https://www.keycloak.org/securing-apps/oidc-layers), ending in `/auth/realms/{realm}`.
55+
- For other OIDC providers, you can usually find the issuer URL by
56+
looking for a "discovery document" or "well-known configuration" at an URL that ends with the suffix `/.well-known/openid-configuration`.
57+
Strip the suffix and use it as the `oidc_issuer_url`.
58+
59+
### Restart SQLPage
60+
61+
When you restart your SQLPage instance, it should automatically contact
62+
the identity provider, find its login URL, and the public keys that will be used to check the validity of its identity tokens.
63+
64+
The next time an user loads page on your SQLPage website, they will be redirected to
65+
the provider's login page. Upon successful login, they will be redirected back to
66+
the page they were initially requesting on your website.
67+
68+
## Access User Information in Your SQL
69+
70+
Once you have successfully configured SSO, you can access information
71+
about the authenticated user who is visiting the current page using the following functions:
72+
- [`sqlpage.user_info`](/functions.sql?function=user_info) to access a particular claim about the user such as `name` or `email`,
73+
- [`sqlpage.user_info_token`](/functions.sql?function=user_info_token) to access the entire identity token as json.
74+
75+
Access user data in your SQL files:
76+
77+
```sql
78+
select 'text' as component, '
79+
80+
Welcome, ' || sqlpage.user_info('name') || '!
81+
82+
You have visited this site ' ||
83+
(select count(*) from page_visits where user=sqlpage.user_info('sub')) ||
84+
' times before.
85+
' as contents_md;
86+
87+
insert into page_visits
88+
(path, user)
89+
values
90+
(sqlpage.path(), sqlpage.user_info('sub'));
91+
```
92+
93+
## Going to Production
94+
95+
When deploying to production:
96+
97+
1. Update the redirect URI in your OIDC provider's settings to:
98+
```
99+
https://your-domain.com/sqlpage/oidc_callback
100+
```
101+
102+
2. Update your `sqlpage.json`:
103+
```json
104+
{
105+
"oidc_issuer_url": "https://accounts.google.com",
106+
"oidc_client_id": "your-client-id",
107+
"oidc_client_secret": "your-client-secret",
108+
"host": "your-domain.com"
109+
}
110+
```
111+
112+
3. If you're using HTTPS (recommended), make sure your `host` setting matches your domain name exactly.
113+
114+
## Troubleshooting
115+
116+
- If login fails, check that the redirect URI matches exactly
117+
- Verify that your client ID and secret are correct
118+
- Make sure your `host` setting matches your application's URL
119+
- For local development, use `http://localhost:8080` as the host
120+
- For production, use your actual domain name

0 commit comments

Comments
 (0)