Who can sign in
Parents, students and staff of the school. Applicants cannot, and neither can a suspended, deactivated or unapproved account. A person always signs in at one school: the school of the key, or for an organization app, the branch you name.
Hosted sign-in: OAuth 2.1 with PKCE
Use this for browser and mobile apps. The person signs in on a Klasstack page branded for their school, approves your app, and is sent back to you with a one-time code. Your app never handles their password. The client is your publishable key, and PKCE with S256 is required.
- Register each redirect URI on the app in the Developer section: https, http on localhost, or an app scheme such as com.example.app:/callback. Up to ten per app, no wildcards.
- Generate a code_verifier of 43 to 128 characters and its S256 code_challenge.
- Send the person to /v1/oauth/authorize. Organization apps add school with the branch id.
- After they approve, your redirect URI receives code and your state. Check state matches.
- Exchange the code at /v1/oauth/token within 60 seconds, from an allowed origin or with your bundle id header. The code works once.
GET https://api.klasstack.com/v1/oauth/authorize
?response_type=code
&client_id=ks_live_pk_your_publishable_key
&redirect_uri=https://app.example.com/callback
&scope=attendance:read
&state=af0ifjsldkj
&code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM
&code_challenge_method=S256curl -X POST https://api.klasstack.com/v1/oauth/token \
-H "Origin: https://app.example.com" \
-d grant_type=authorization_code \
-d client_id=ks_live_pk_your_publishable_key \
-d code=ks_ac_code_from_the_redirect \
-d redirect_uri=https://app.example.com/callback \
-d code_verifier=your_original_code_verifier{
"access_token": "eyJhbGciOi...",
"token_type": "Bearer",
"expires_in": 900,
"refresh_token": "ks_rt_your_refresh_token",
"scope": "attendance:read"
}The OAuth endpoints answer errors in the OAuth shape, error and error_description, with codes such as invalid_request, invalid_client, invalid_grant, invalid_scope, unauthorized_client, unsupported_grant_type and access_denied. A sign-in request left open for ten minutes expires.
Direct login and OTP
For a trusted first-party app with its own sign-in screen, your server can sign the person in with the app secret key. The person types their details into your app, so only use this where they already trust you with them.
| Route | Body | What it does |
|---|---|---|
| POST /v1/auth/login | accountKind, identifier, password, twoFactorCode (when asked) | Password sign-in. accountKind is portal for parents and students, staff for staff. |
| POST /v1/auth/otp/request | accountKind, identifier | Sends a one-time code. It always answers the same way, so it never reveals whether an account exists. |
| POST /v1/auth/otp/verify | accountKind, identifier, otp | Signs in with the code. |
curl -X POST https://api.klasstack.com/v1/auth/login \
-H "Authorization: Bearer ks_live_sk_your_key_here" \
-H "Idempotency-Key: 0f4e8a52-9d1b-4c6e-8a7f-3b2c1d0e9f8a" \
-H "Content-Type: application/json" \
-d '{"accountKind": "portal", "identifier": "ada.parent@example.com", "password": "their password"}'A successful sign-in returns accessToken, refreshToken, tokenType, expiresIn, refreshExpiresOn, userId and audience (parent, student or staff).
Calling the API as the person
Send the access token as the bearer on its own, with no app key. It lasts 15 minutes.
curl https://api.klasstack.com/v1/me/attendance \
-H "Authorization: Bearer the_access_token"The audience ceiling
What a person token can do is the overlap of three things: the app's services, the ceiling for the person's audience, and the person's own links and role. The token never reaches further than the person could in the Klasstack app itself.
| Audience | Ceiling |
|---|---|
| parent | Their own linked children only. |
| student | Their own records only. Students are children, so this is the narrowest set. |
| staff | Their existing role in the school, capped by the app services. |
Refresh and logout
- Refresh before the access token expires. Hosted sign-in apps call /v1/oauth/token with grant_type=refresh_token and client_id. Direct login apps call POST /v1/auth/token/refresh with the secret key and refreshToken.
- A refresh token lasts 30 days and works once: each refresh returns a new pair. Keep the new one.
- Using a refresh token twice ends the whole session with refresh_token_reused. Refresh from one place in your app only.
- Sign a person out with POST /v1/auth/logout and their refreshToken. That ends the session and its tokens.