Skip to main content
PhosraLinkKit is the native-iOS Phosra Link — a Phosra-branded Connect sheet a parental-controls app (PCA) presents in about ten lines, with onSuccess / onExit callbacks. It’s the iOS peer of the web/React-Native @phosra/connect drop-in, and it drives the same @phosra/link ceremony — init → complete → bind → grant — through your own backend (BFF). The parent’s authentication and your OCSS signing key never leave your app (self-custody by default). The SDK holds no keys; your BFF signs the consent attestation. Same trust model as the rest of OCSS, same ergonomics as Plaid’s iOS SDK.

Install

Swift Package Manager, iOS 16+:
.package(url: "https://github.com/Phosra-Inc/phosra-link-kit-ios.git", from: "0.1.0")
During monorepo development you can also depend on it by path (.package(path: "packages/ios-connect")).

Quickstart

import PhosraLinkKit

let config = PhosraLinkConfiguration(
    bffBaseURL:   URL(string: "https://app.yourpca.com")!,   // your backend
    sessionToken: myParentSessionToken,                      // the token YOUR login issued
    platform:     ConnectPlatform(did: "did:ocss:snaptr", name: "Snaptr"),
    rules: [
        ConnectRule(category: "addictive_pattern_block", label: "Turn off the infinite feed"),
        ConnectRule(category: "dm_restriction",          label: "Limit who can message them"),
    ],
    grantedScope: ["addictive_pattern_block", "dm_restriction"],
    redirectUri:  "yourapp://phosra-link",                   // a custom scheme your app registers
    childId:      "child:uuid"                               // optional
)

let handler = PhosraLink(configuration: config)
handler.present(
    from: self,                                              // any UIViewController
    onSuccess: { success in
        print("granted:", success.grantId)                  // the OCSS consent grant
    },
    onExit: { exit in
        // .userCanceled or .error — no rules were changed
    }
)
That’s the whole integration. Exactly one of onSuccess / onExit fires per presentation — including if the presenter is torn down out from under the sheet — and you do not need to retain the handler (it self-retains while presented).
The platform’s OAuth runs in a system ASWebAuthenticationSession, so your app never sees the parent’s platform credentials. The Connect sheet renders in your app — there is no Phosra-hosted page or web view.

End-to-end

The whole integration on one page — client and backend:
1

Register your redirect scheme

Add your custom scheme (e.g. yourapp) to the app target’s Info.plist CFBundleURLTypes, so ASWebAuthenticationSession can receive the platform’s OAuth callback at yourapp://phosra-link.
2

Authenticate the parent (your own login)

The parent signs in with your app’s auth — Phosra never sees their credentials. On success, mint a server-side session token (the reference BFF issues a signed phosra_parent cookie via issueParentSession). That token is the sessionToken you pass the SDK. Never accept it from the client.
3

Stand up the 3 BFF routes

POST /connect/init | /connect/complete | /connect/bind, each wrapping @phosra/link server-side — see the reference BFF. Your bind route signs the consent with your OCSS key and returns { grant_id }.
4

Present the sheet

Build PhosraLinkConfiguration (the Quickstart above) and call PhosraLink(configuration:).present(from:onSuccess:onExit:).
5

Handle the result

onSuccess delivers the grantId — persist it and drive rule directives with @phosra/link’s directive(...). onExit means the parent canceled or errored; nothing changed.
Metered census usage requires a Phosra API key — provision one in the developer console or via POST /api/v1/developers/orgs/{orgID}/keys.

What your backend provides

PhosraLinkKit never talks to Phosra directly. It calls your BFF — the three routes the reference BFF exposes, each wrapping @phosra/link server-side:
RouteWraps (@phosra/link)
POST /connect/initinitPlatformOAuth{ authorizeUrl, state, sessionId }
POST /connect/completecompletePlatformOAuth{ sessionId, childProfiles }
POST /connect/bindbindProfilerunConnectCeremony{ grant_id }
Your bind route signs the consent attestation with your OCSS key and posts it to the census. The SDK relays your parent-session token (a phosra_parent cookie by default; override sessionHeaderName / sessionHeaderValue for a bearer). This is Plaid’s link_token pattern — a thin backend mints the session and finalizes the signature.

Platform logos

The sheet shows the platform’s verified logo — the one from its accredited Trust-List entry, not a logo the app pastes in. GET /api/v1/providers/{did}/connect returns the provider’s name and icon_url (from the registry); pass that URL into ConnectPlatform:
ConnectPlatform(
    did: "did:ocss:snaptr",
    name: "Snaptr",
    logoURL: URL(string: providerIconURL) // from /providers/{did}/connect → icon_url
)
The sheet renders it, falling back to a generic glyph while it loads or if it’s missing. For a bundled asset instead of a URL, pass platformLogo: on the configuration. Because the logo comes from the accredited registry, the logo you see is the one Phosra verified — it’s part of the provider’s identity, not decoration.

The honesty contract

The sheet ports the approved design’s honesty contract verbatim, and the SDK cannot show a fake green:
  • The header reads phosra · OCSS (a subordinate mark) and the trust claim is the precise “Accredited on the OCSS Trust List” — Phosra is an accredited router, not OCSS itself.
  • Success shows “Verified on the OCSS Trust List” — the only green — and only after the server binds the grant.
  • On failure: “No rules were changed — you can try again.”
Load-bearing invariant: your bind route must return a grant_id only once the consent is minted and verified to the OCSS root. The green “Verified” rests on that signal alone — return an unverified grant_id and you have made the SDK show a fake green.

States

The sheet walks: Connect (intro + accreditation) → What gets applied (the rules preview) → the platform’s OAuth → Choose accountVerified success — or an honest error at any step. All rendered natively (SwiftUI); the marks (the Phosra spark, the wordmark, the OCSS glyph) ship as source and tint by color, so there is no asset bundle to manage.

Advanced

  • Custom transport. The 4-argument PhosraLinkConfiguration.init takes your own ConnectTransport if you route the three calls differently.
  • Fresh platform login. PhosraLinkConfiguration(prefersEphemeralSession: true) forces a new platform sign-in (no shared cookies).
  • Granular events. Pass onEvent to observe each ceremony transition.

Notes

  • iOS 16+. The core (controller, transport, sheet) is UI-framework-agnostic and unit-tested; the ASWebAuthenticationSession presenter is iOS-only.
  • MIT-licensed. All signing and verification are your backend’s; metered census usage requires a Phosra API key, billed server-side — never in this package.