This guide shows how to let the people who use your app sign in with their Manabu2 ID and password. They type the password on a Manabu2 screen, so it never passes through your app.
How it works
- Your app generates a PKCE pair
- It sends the user to the Manabu2 sign-in screen
- After sign-in, your app receives an authorization code
- Your app exchanges the code for tokens
- It calls the API with the tokens
Step 1: Generate a PKCE pair
For every sign-in, create a random string (the verifier) and its hash (the challenge).
code_verifier = base64url(32–64 random bytes) // keep this inside the app code_challenge = base64url(sha256(code_verifier)) // this is all you send state = base64url(16 random bytes) // keep it and check it on the way back
Because only the hash is sent, someone who intercepts the traffic still can't redeem the code. state lets you reject requests your app didn't start.
Step 2: Send the user to the sign-in screen
GET https://manabu2.com/connect/authorize ?client_id=<your registered client ID> &redirect_uri=<a registered redirect URI (URL-encoded)> &response_type=code &scope=read%3Acatalog%20read%3Acontent%20read%3Aprogress%20write%3Aprogress &state=<state from step 1> &code_challenge=<challenge from step 1> &code_challenge_method=S256
ASWebAuthenticationSession; on Android, Chrome Custom Tabs.
Users who are already signed in to Manabu2 aren't asked for their ID and password again; they just approve the consent screen and go straight back to your app. That's single sign-on.
Step 3: Receive the code
acme://auth/callback?code=f5kIhYnGah1I…&state=<the value you sent>
state matches the value you sent.
If it doesn't, stop and don't exchange the code.
Step 4: Exchange the code for tokens
POST https://api.manabu2.com/api/v1/oauth/token Content-Type: application/x-www-form-urlencoded grant_type=authorization_code &code=<the code you received> &client_id=<client ID> &redirect_uri=<same value as step 2> &code_verifier=<verifier from step 1>
You can send a JSON body instead. Confidential clients must also send client_secret. The response contains access_token, token_type, expires_in, refresh_token and scope.
Step 5: Call the API
GET https://api.manabu2.com/api/v1/courses Authorization: Bearer <access_token>
The access token's lifetime is returned in expires_in (in seconds). It's currently 24 hours, but it can change, so use this value rather than hard-coding one.
Three common pitfalls
Every call to POST /api/v1/auth/refresh (or to POST /api/v1/oauth/token with grant_type=refresh_token) returns a new refresh token and invalidates the old one. Always save the new value you get back.
http versus https, or a different port number is enough to get the request rejected. You also need to send exactly the same string in step 2 and step 4.
Each call to POST /api/v1/lessons/{id}/quiz/submit creates a new attempt. If you resend after a network timeout, the same submission gets recorded twice.
Idempotency-Key: 9f1c2d4e-7a3b-4c11-9e55-2b8d0f6a1c37
If you resend with the same key, you get the original response back unchanged. Attempts count toward certificate requirements, so be especially careful here.
The consent screen for third-party apps
For apps your organization registers in Manabu2, a consent screen appears every time a user signs in. It shows the app's name and what it's asking to do, and the user chooses Allow or Cancel.
Your own registered apps get a consent screen too, because Manabu2 didn't write them. It makes clear to the user which app they're allowing to do what.