Skip to content

Add OIDC offline_token user sync#73

Open
schliz wants to merge 13 commits intoHaDiNet:masterfrom
schliz:feat/ical-subscription
Open

Add OIDC offline_token user sync#73
schliz wants to merge 13 commits intoHaDiNet:masterfrom
schliz:feat/ical-subscription

Conversation

@schliz
Copy link
Contributor

@schliz schliz commented Mar 15, 2026

Create a system for persisting offline_tokens for users that logged in via OpenID Connect. We can use this to periodically update the groups for users that haven't signed in for a while.

Resolves #41

@pablo-schmeiser pablo-schmeiser added the enhancement New feature or request label Mar 15, 2026
@schliz
Copy link
Contributor Author

schliz commented Mar 15, 2026

closes #36

Copy link
Collaborator

@pablo-schmeiser pablo-schmeiser left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very important and awaited feature!
Looks good to me 👍

Once @hd1ex approves this too, this is ready to be merged.
If @lewellien is still interested in this topic: They may want to review this as well.

@pablo-schmeiser pablo-schmeiser removed the request for review from mfbehrens March 15, 2026 18:26
@pablo-schmeiser pablo-schmeiser linked an issue Mar 15, 2026 that may be closed by this pull request
@hd1ex
Copy link
Collaborator

hd1ex commented Mar 15, 2026

Thanks for working on iCal support!
I didn't look at everything but will do. Overall it looks good to me, but I haven't tested it yet.

My first question is: Does this also fix or works upon fixing #41?
Meaning that it also adds the ability to communicate with the identity provider?
It feels like we'd just have to add a systemd timer to fix the issue.

Can you split the OIDC stuff to another change? I'd rather review and integrate these two things step by step.

Copy link
Collaborator

@hd1ex hd1ex left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See comment above.

Move _populate_user from AuthorizeSSOUser view into
accounts/oidc.py as populate_user_from_oidc so it can be
reused by the offline token refresh logic without a
model-to-view circular dependency.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@schliz schliz force-pushed the feat/ical-subscription branch from e0f7a40 to 8eb1778 Compare March 15, 2026 21:54
@schliz schliz changed the title Add token-based iCal calendar subscriptions with OIDC offline token refresh Add OIDC offline_token user sync Mar 15, 2026
schliz and others added 12 commits March 15, 2026 23:00
CalendarToken stores a unique per-user token for session-less
iCal feed access. OIDCOfflineToken stores the OIDC refresh
token and implements refresh_user_info() to sync groups from
the identity provider before serving calendar data.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Introduce TokenAuthMixin that authenticates feed requests via
a CalendarToken URL parameter and refreshes OIDC user data
with a 10-minute cooldown. Register token feed URLs under
/calendar/token/<token>/{user,participation,organization,event}/.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add token management views (create/regenerate/delete) and a
Calendar Subscriptions card on the profile page showing
copyable iCal URLs with regenerate and revoke controls.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Move the Calendar Subscriptions card from the left sidebar into
the right column above Upcoming Shifts for better visibility.
Add "Add to Calendar" buttons using the webcal:// protocol so
users can subscribe with a single click from their device.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Combine the "Add to Calendar" button and the copyable URL input
into one flex row per feed for a more compact layout.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Use the same card structure (p-3, center-items header row) as
the user info card. Move regenerate and revoke buttons into the
title row as icon-only buttons, and show the generate button
there when no token exists.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add German translations for all calendar subscription strings.
Rename "My Participations" to "My Shifts" for clarity. Increase
spacing between subtitle text and feed sections.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Remove all obsolete (#~) entries including calendar-related strings
and fix duplicate "My Shifts" entries that caused msgmerge errors.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@schliz schliz force-pushed the feat/ical-subscription branch from 8eb1778 to 11c13e0 Compare March 15, 2026 22:05
@schliz schliz requested a review from hd1ex March 15, 2026 22:10
@schliz
Copy link
Contributor Author

schliz commented Mar 15, 2026

@hd1ex you can create a timer that runs src/shiftings/accounts/management/commands/sync_oidc_users.py for the desired functionality described in #41

@schliz schliz force-pushed the feat/ical-subscription branch from 3691cff to 11c13e0 Compare March 15, 2026 23:09
Comment on lines +23 to +44
tokens = OIDCOfflineToken.objects.select_related('user').all()
total = tokens.count()
success = 0
failed = 0

self.stdout.write(f'Syncing {total} OIDC user(s)...')

for offline_token in tokens:
username = offline_token.user.username
if offline_token.refresh_user_info():
success += 1
logger.info('Synced user %s', username)
else:
failed += 1
logger.warning('Failed to sync user %s', username)
if options['purge_expired']:
offline_token.delete()
logger.info('Purged expired token for user %s', username)

self.stdout.write(self.style.SUCCESS(
f'Done. {success} synced, {failed} failed (of {total} total).'
))
Copy link
Collaborator

@hd1ex hd1ex Mar 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you also print out the time how long a refresh run took, so an admin can get a feeling of the performance impact? I also saw some async stuff was added to Django recently. I don't know if it is easy to add but it maybe an option to improve overall reactiveness, if this task is IO heavy.

When does such a token expire? Do I understand it correctly that we have to refresh the token regularly and expired tokens can only be "refreshed" if the user does relogin?

If so, this only solves #41 for future deployments.
On a running system no tokens are there (yet?) so this implies for me some kind of purge when this change is rolled out:

  • Terminate all user sessions (forcing them to relogin)
  • Update all group memberships by
    • either clearing them all and let the users log back in to rejoin
    • or using some kind of other data source (e.g. LDAP). This also does not solve the problem for inactive users, so I am in favor of the first option.

Maybe some scripting/documentation to support this would be nice.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The offline token expiry is handled by the OIDC IdP. This implementation currently does nothing on expiry. Ways to solve this is adding a grace period until the 30. June and begin clearing group associations and requiring re-authentication from then. The HaDiKo deployment of Shiftings should use quite short token periods which would force everybody using the App to have logged in at least once since then.

@pablo-schmeiser pablo-schmeiser self-requested a review March 16, 2026 22:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Removed members of a group stay in that group until they log in again Webcal

3 participants