Sign-in and LTI
Hanami accepts an A+ personal API token in two sign-in flows:
- The token prompt lets a user paste the token from their A+ profile.
- An A+ LTI 1.1 menu launch can carry the token as
custom_user_api_tokenwhen API access is enabled for the LTI service.
Both flows send the A+ token once from SvelteKit to POST /internal/frontend/sessions. Ktor validates it, resolves the A+ identity, encrypts the token, and returns a random Hanami session identifier. SvelteKit stores that identifier in the hanami_session cookie. The raw A+ token is not written to a browser cookie.
LTI launch validation
Section titled “LTI launch validation”The LTI launch is a top-level cross-site POST. SvelteKit admits form posts only from the exact HTTP(S) origin configured by APLUS_URL, then validates the OAuth 1.0 signature, nonce, timestamp, consumer key, and signed launch URL before exchanging the A+ token. The signing method is HMAC-SHA1 because that is what the A+ LTI 1.1 integration uses.
The origin allowlist limits which site can reach the launch handler; the OAuth signature authenticates the launch. Other browser mutations use SvelteKit remote functions and the normal same-origin policy.
A+ configuration
Section titled “A+ configuration”Create an LTI 1.1 service in the A+ site administration and grant it API access. Configure the same values in Hanami:
LTI_CONSUMER_KEYLTI_CONSUMER_SECRETAPLUS_URL
Add the service to each course menu with the label Hanami and the postcard-heart icon. Ktor discovers a launch URL for each course. Set LTI_MENU_LABEL if the menu uses a different label.
The consumer key and secret must be configured together in SvelteKit. Production rejects the well-known local development secret unless LTI_ALLOW_WEAK_SECRET=true is set explicitly.
Browser requirements
Section titled “Browser requirements”Open Hanami as a full page, not in an iframe. The session cookie uses SameSite=Lax, and modern browsers restrict third-party cookie use inside cross-site frames.
The first A+ launch may show a consent page because A+ is sending identity data and API access to another service. Users can allow automatic continuation for later launches.
Troubleshooting
Section titled “Troubleshooting”Course settings reports no LTI launch
Section titled “Course settings reports no LTI launch”Check that the course menu contains the configured service and that its label matches LTI_MENU_LABEL. Then use Refresh from A+ in course settings.
Signature validation fails
Section titled “Signature validation fails”Check that the consumer key and secret match the A+ site service. The reverse proxy must forward the public scheme and host. Set LTI_LAUNCH_URL only when the URL signed by A+ differs from the URL SvelteKit reconstructs from forwarded headers.
A+ opens but does not return to Hanami
Section titled “A+ opens but does not return to Hanami”Use a full-page menu link and accept the first-launch consent page. Confirm that FRONTEND_ORIGIN is the public Hanami origin and that the browser can reach both sites.
Manual sign-in works but LTI does not
Section titled “Manual sign-in works but LTI does not”The session exchange is shared, so the fault is in the LTI service, menu item, signature, nonce, or launch URL. Check the SvelteKit log by request ID without logging the launch body or token.
For local setup, see Local A+.