✨ feat(auth): accept an instance's terms instead of dead-ending on them - #51
Merged
Conversation
Signing in to a server that requires its terms showed "Login is restricted. Check with your administrator." — wrong advice, and no way forward. PLANKA answers a refused sign-in with 403 and distinguishes three situations through the body's `message`: terms acceptance required, SSO-only, and an instance no admin has initialised yet. `PlankaClient` threw all of them away (`case 403: throw .forbidden`), so the app could not tell them apart, let alone act on them. - Parse the restriction into `AuthRestriction`, carrying the reason and the `pendingToken` that resumes the flow. Both `pendingToken` and `step` are missing from planka-openapi.json but are returned in practice, so this was derived from a real response rather than the spec. - An ordinary permission 403 still throws `.forbidden`: only bodies that actually describe a sign-in restriction qualify, or every "not your board" error would start reading as a login problem. - Add `getTerms(language:)` and `acceptTerms(pendingToken:signature:)`, plus query-item support in `buildRequest` — a query folded into `path` lands in `URLComponents.path` and gets percent-encoded into the URL. - Add `TermsView`: renders the instance's Markdown as server data, accepts, and activates the profile only once a token is stored. Reachable from both the password and SSO legs. - Give each restriction its own copy. Telling someone to contact an administrator when the fix is to press the SSO button helps nobody. Also drops two catalog keys the extraction pass flagged as stale; neither string exists in the code any more. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Found in the first TestFlight build: signing in to a server that requires its terms shows "Login is restricted. Check with your administrator." — advice that is both wrong and a dead end.
What PLANKA actually says
A refused sign-in comes back as 403, and the body's
messagedistinguishes three situations:messageTerms acceptance requiredUse single sign-onAdmin login required to initialize instancePlankaClientthrew all of it away —case 403: throw .forbidden— so the app could not tell them apart.Not derivable from the spec
POST /access-tokens/accept-termsneeds apendingToken, butplanka-openapi.jsondocuments onlycodeandmessageon the 403. A real instance returns:{"code":"E_FORBIDDEN","pendingToken":"…","message":"Terms acceptance required","step":"accept-terms"}pendingTokenandstepare undocumented. This was built against that observed response, and the test fixture mirrors it.Changes
BoardlyKit
AuthRestrictioncarries the reason and the pending token;PlankaAPIError.authRestrictionsurfaces it..forbidden— only bodies describing a sign-in restriction qualify, or every "not your board" error would start reading as a login failure.getTerms(language:)andacceptTerms(pendingToken:signature:).buildRequestgains query items. A query string folded intopathlands inURLComponents.pathand is percent-encoded into the URL, silently requesting something that does not exist.App
TermsViewrenders the instance's Markdown as server data (never localized), and activates the profile only once a token is stored — consistent with the invariant 🐛 fix(auth): stop entering a session for a server that was never logged into #50 just restored.login/completeSSOreturn aLoginOutcomerather than aBool, since valid credentials can now yield a half-finished session.Verification
fr).Unexpected error: %@andAdded to "%@"no longer exist anywhere in the code. A third stale flag, the OIDCauthorizationUrlerror, was a false positive — that string is still used atLoginView.swift:90and is kept.Not verified
The screen has not been exercised against a live instance that demands terms, nor rendered in the simulator. The Markdown is parsed inline-only (
AttributedStringdoes not do block syntax), so headings in a server's terms will read as plain lines.🤖 Generated with Claude Code