Skip to content

feat(authguest): Renew and PIN endpoints - #3667

Open
maki5 wants to merge 15 commits into
opencloud-eu:mainfrom
maki5:feat/authguest_renewal
Open

maki5 wants to merge 15 commits into
opencloud-eu:mainfrom
maki5:feat/authguest_renewal

Conversation

@maki5

@maki5 maki5 commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor

closes: #3625

Renewal and PIN verification endpoints

  • POST .../guestLinks/renew — issues a new link token and a one-time PIN for an existing guest link.
  • POST .../guestLinks/verify/pin — exchanges the PIN for a fresh session.
  • Renew publishes a new GuestTokenRenewed event carrying the token and PIN.

dependent on: opencloud-eu/libre-graph-api#78

@codacy-production

codacy-production Bot commented Oct 6, 2026 •

Copy link
Copy Markdown

Not up to standards ⛔

🔴 Issues 1 minor

Alerts:
⚠ 1 issue (≤ 0 issues of at least minor severity)

Results:
1 new issue

Category Results
CodeStyle 1 minor

View in Codacy

🟢 Metrics 169 complexity

Metric Results
Complexity 169

View in Codacy

🟢 Coverage 53.94% diff coverage · +0.12% coverage variation

Metric Results
Coverage variation ✅ +0.12% coverage variation (-1.00%)
Diff coverage ✅ 53.94% diff coverage

View coverage diff in Codacy

Coverage variation details
Coverable lines Covered lines Coverage
Common ancestor commit (ba98b1d) 91152 22423 24.60%
Head commit (7f6d24a) 91573 (+421) 22640 (+217) 24.72% (+0.12%)

Coverage variation is the difference between the coverage for the head and common ancestor commits of the pull request branch: <coverage of head commit> - <coverage of common ancestor commit>

Diff coverage details
Coverable lines Covered lines Diff coverage
Pull request (#3667) 545 294 53.94%

Diff coverage is the percentage of lines that are covered by tests out of the coverable lines that the pull request added or modified: <covered lines added or modified>/<coverable lines added or modified> * 100%

NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.

return ErrEventsNotConfigured
}

share, err := s.validateShare(ctx, shareID)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Hm, when only requiring knowledge of the share id, which isn't particularly secret, anyone who knows that ID could terminate existing sessions.
I think we could maybe prevent this by requiring either the last token or the (expired) session here?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

you mean sessionToken?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Right. Basically something only the user of the current session (=guestlink recipient) would know.

if err := s.store.Redeem(rec.ShareIDHash); err != nil {
if errors.Is(err, storage.ErrAlreadyRedeemed) {
return nil, &RedeemError{ErrorType: ErrAlreadyRedeemed, ShareID: rec.ShareID}
if err := s.store.Update(rec.ShareIDHash, func(r *storage.Record) error {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

With the option to create new tokens in Renew there's a little toctou race here now. After verifying the token and the share we only lock the record in Update, so if a renew happens in between the callback marks the new record as redeemed instead of the previous one.
We should add another check in the callback to catch those cases.

Pin: pinStr,
Timestamp: now,
}); err != nil {
if rbErr := s.store.Update(token.Hash(shareID), func(rec *storage.Record) error {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Similarly to the race described above, we should only roll back if the record on disk still matches what we were working with in the first place

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Actually... maybe the update function itself should verify the record after locking instead of relying on all callers to be thorough.
We could implement it by storing a revision number in the records and adding a UpdateFrom(seen Record, fn func(*Record) error) (Record, error) function which verifies that the revision matches after grabbing the lock, for example (just a suggestion)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

that's actually a good idea

}

func Hash(p string) (string, error) {
if p == "" {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

nit: we could verify the length against Length instead.

@aduffeck aduffeck left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

lgtm 👍

@aduffeck

aduffeck commented Oct 7, 2026

Copy link
Copy Markdown
Member

needs a rebase, but the code itself looks good to me.

@maki5
maki5 force-pushed the feat/authguest_renewal branch from f1197b9 to 63545a7 Compare October 7, 2026 11:25
@maki5
maki5 force-pushed the feat/authguest_renewal branch from cb104d7 to 7f6d24a Compare October 8, 2026 10:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

guestlinks: token renewal with and without PIN

2 participants