Skip to content

feat(auth): request highlights permission at sign-in#280

Merged
cameronapak merged 2 commits into
mainfrom
claude/gallant-williamson-27adac
Jul 8, 2026
Merged

feat(auth): request highlights permission at sign-in#280
cameronapak merged 2 commits into
mainfrom
claude/gallant-williamson-27adac

Conversation

@cameronapak

@cameronapak cameronapak commented Jul 8, 2026

Copy link
Copy Markdown
Collaborator

What

Lets apps request the highlights permission (and future data-exchange permissions) at sign-in.

Why it's not a scope

highlights is deliberately not an OIDC scope. Per the YouVersion authorize contract, it rides alongside scope as a repeatable query param and is authorized via a separate per-app ACL:

/auth/authorize?...&scope=openid+profile+email&requested_permissions[]=highlights

Scopes stay generic/portable (profile, email); bespoke YouVersion data (highlights, and later prayer_requests, etc.) lives in requested_permissions[].

Changes

Layer Change
core signIn(url, scopes?, permissions?) + PKCE builder append requested_permissions[]
hooks useYVAuth().signIn({ permissions })
ui <YouVersionAuthButton permissions={['highlights']} />

Scopes and permissions are separate args. Existing scope-only calls are unaffected.

Test plan

  • Core PKCE + Users suites (53 pass) — asserts requested_permissions[], multiple perms, omitted-when-empty, and not folded into scope
  • Hooks useYVAuth (19 pass)
  • pnpm typecheck clean, Prettier + ESLint clean
  • Changeset added (minor, all 3 packages)

Not in scope

Grant verification after callback. This wires up requesting; how a consumer confirms the grant (token claim / userinfo / ACL call) is unresolved and left for a follow-up once the backend contract is known.

🤖 Generated with Claude Code

Greptile Summary

This PR adds an optional permissions parameter to the sign-in stack — core PKCE builder, YouVersionAPIUsers.signIn, useYVAuth, and YouVersionAuthButton — so apps can request YouVersion data-exchange permissions (starting with highlights) at authorize time. The new requested_permissions[] query params ride alongside OIDC scope as the backend contract requires and are kept fully separate from it.

  • Core: SignInWithYouVersionPKCEAuthorizationRequestBuilder appends each permission as a requested_permissions[] param; three new test cases cover single permission, multiple permissions, and the empty/omit case.
  • Hooks: useYVAuth().signIn accepts a permissions array and forwards it via optional chaining, replacing the previous redundant if/else branch.
  • UI: YouVersionAuthButton exposes a permissions prop typed as SignInWithYouVersionPermissionValues[]; all three packages receive a minor changeset bump.

Confidence Score: 5/5

Additive change with no impact on existing call sites; permissions are appended as typed, URL-encoded query params and never mixed into the OIDC scope string.

The change is backward compatible at every layer, the new code paths are exercised by targeted unit tests (including a guard that highlights never appears in scope), and the PKCE URL builder uses URLSearchParams.append which handles encoding correctly.

No files require special attention; the only suggestion is adding SignInWithYouVersionPermissionValues to the UI package re-export list for a complete public surface.

Important Files Changed

Filename Overview
packages/core/src/SignInWithYouVersionPKCE.ts Adds permissions parameter to make and authorizeURL; appends each permission as a requested_permissions[] query param. Logic is correct and well-commented.
packages/core/src/Users.ts Threads the optional permissions array through YouVersionAPIUsers.signIn to the PKCE builder. Backward compatible; no logic issues.
packages/hooks/src/useYVAuth.ts Adds permissions to the signIn params object and forwards it via optional chaining, replacing the previous redundant if/else branch. Clean and correct.
packages/ui/src/components/YouVersionAuthButton.tsx Exposes permissions prop and passes it through to signIn. JSDoc example imports SignInWithYouVersionPermission but the existing docblock example doesn't demonstrate its usage — minor pre-existing doc gap.
packages/core/src/tests/SignInWithYouVersionPKCE.test.ts Adds three new test cases: single permission, multiple permissions, and omit-when-absent. Also verifies permissions don't leak into the OIDC scope param. Good coverage.
packages/hooks/src/useYVAuth.test.tsx Updates existing assertions to match the new always-3-arg call shape and adds a new test for permissions forwarding. All assertions align with the updated implementation.
.changeset/request-highlights-permission.md Marks all three packages as minor bumps, which is appropriate for a backward-compatible additive API change.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant App
    participant YouVersionAuthButton
    participant useYVAuth
    participant YouVersionAPIUsers
    participant PKCEBuilder as SignInWithYouVersionPKCEAuthorizationRequestBuilder
    participant Browser

    App->>YouVersionAuthButton: "render permissions={['highlights']}"
    YouVersionAuthButton->>useYVAuth: "signIn({ scopes, permissions })"
    useYVAuth->>YouVersionAPIUsers: signIn(url, scopes, permissions)
    YouVersionAPIUsers->>PKCEBuilder: make(appKey, redirectURL, scopes, permissions)
    PKCEBuilder->>PKCEBuilder: authorizeURL(..., scopes, permissions)
    Note over PKCEBuilder: queryParams.set('scope', 'openid profile')<br/>queryParams.append('requested_permissions[]', 'highlights')
    PKCEBuilder-->>YouVersionAPIUsers: "{ url, parameters }"
    YouVersionAPIUsers->>Browser: "window.location.href = url"
    Note over Browser: /auth/authorize?scope=openid+profile&requested_permissions%5B%5D=highlights
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant App
    participant YouVersionAuthButton
    participant useYVAuth
    participant YouVersionAPIUsers
    participant PKCEBuilder as SignInWithYouVersionPKCEAuthorizationRequestBuilder
    participant Browser

    App->>YouVersionAuthButton: "render permissions={['highlights']}"
    YouVersionAuthButton->>useYVAuth: "signIn({ scopes, permissions })"
    useYVAuth->>YouVersionAPIUsers: signIn(url, scopes, permissions)
    YouVersionAPIUsers->>PKCEBuilder: make(appKey, redirectURL, scopes, permissions)
    PKCEBuilder->>PKCEBuilder: authorizeURL(..., scopes, permissions)
    Note over PKCEBuilder: queryParams.set('scope', 'openid profile')<br/>queryParams.append('requested_permissions[]', 'highlights')
    PKCEBuilder-->>YouVersionAPIUsers: "{ url, parameters }"
    YouVersionAPIUsers->>Browser: "window.location.href = url"
    Note over Browser: /auth/authorize?scope=openid+profile&requested_permissions%5B%5D=highlights
Loading

Reviews (2): Last reviewed commit: "refactor(hooks): collapse redundant sign..." | Re-trigger Greptile

Wire the `highlights` permission through the auth flow. It rides alongside
OIDC scopes as a repeatable `requested_permissions[]` query param, not as a
scope, matching the YouVersion authorize contract.

- core: signIn/PKCE builder accept an optional `permissions` array
- hooks: useYVAuth().signIn({ permissions })
- ui: <YouVersionAuthButton permissions={['highlights']} />

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@changeset-bot

changeset-bot Bot commented Jul 8, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 9fb7e13

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 4 packages
Name Type
@youversion/platform-core Minor
@youversion/platform-react-hooks Minor
@youversion/platform-react-ui Minor
vite-react Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

Comment thread packages/hooks/src/useYVAuth.ts Outdated
Passing undefined for optional params is identical to omitting them, so
the if/else branch produced the same authorize URL either way. Collapse
to a single call and update the three tests that asserted the bare
signIn(url) shape.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@cameronapak cameronapak merged commit 683c123 into main Jul 8, 2026
6 checks passed
@cameronapak cameronapak deleted the claude/gallant-williamson-27adac branch July 8, 2026 18:10
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.

2 participants