OAuth2 guide: Continue with Moymo
Moymo is an OAuth 2.0 authorization server. Your application can send a member to Moymo, they decide what to share, and your application gets a token it can use for exactly that.
The Developer Portal itself signs you in this way: it is the first application to use the flow described here.
When to use it
- Sign in to your site or app with Moymo. Ask for
identifyand you get the member's id, username, display name and picture. - Read something about the member that they agree to share: their e-mail (
email), the servers they are in (servers). - Add your bot to a server they manage (
bot). This is how a bot is installed.
The scopes are listed in Scopes.
The flow
It is the standard authorization code flow, with PKCE.
The six stepsText
text
your app member's browser Moymo
| 1. send to /oauth2/authorize ---------------------------------->|
| 2. member signs in, reads the |
| consent screen, says yes |
|<--- 3. redirect to your redirect_uri?code=...&state=... --------|
| 4. POST /api/v1/oauth2/token (code, verifier or secret) ------>|
|<--- 5. access token + refresh token ----------------------------|
| 6. GET /api/app/v1/users/@me (Authorization: Bearer ...) ----->|1. Register
In the Developer Portal, on your application's OAuth2 tab:
- note the client id;
- add every redirect address you will use. A redirect has to be exact, character for character. Allowed:
https://...;http://localhost,http://127.0.0.1orhttp://[::1]with any port (for development); or a native app's own scheme likecom.example.app:/callback(a reversed domain, as RFC 8252 asks). No wildcards, no#fragment, no password in the address; - if your code runs on a server that can keep a secret, make a client secret. If it runs in a browser or on a phone, you use PKCE and have no secret.
2. Send the member to Moymo
The link to send the member to (on one line; broken here to be read)Text
text
https://moymo.net/oauth2/authorize
?client_id=0f6c1a52-3b9e-4c1c-9d1b-6e7d2a8f4b10
&redirect_uri=https%3A%2F%2Fexample.com%2Fcallback
&response_type=code
&scope=identify%20servers
&state=9d3a7c...
&code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM
&code_challenge_method=S256| Parameter | |
|---|---|
client_id | Your application's id. |
redirect_uri | One of the addresses you registered. May be left out if you registered exactly one. |
response_type | code. |
scope | The scopes you want, separated by spaces (or +, or commas). Only scopes that exist. |
state | A random value of yours. It comes back unchanged; check it: it is what stops somebody else's authorisation from being delivered to your session. |
code_challenge, code_challenge_method | PKCE. S256 only. See below. |
The member sees your application's name, picture and summary, who made it, its verified domain, and each scope in plain words, and chooses Authorise or Cancel. If they are not signed in to Moymo they sign in first and come back.
Drafts
A draft application can be authorised by its owner and its testers only. Anyone else gets access_denied. See Publishing.
3. Receive the code
Moymo sends the member's browser to your redirect address:
Where the member's browser landsText
text
https://example.com/callback?code=Qp2hY...&state=9d3a7c...or, if they said no, with error=access_denied and the same state. A code works once, for 5 minutes, for this redirect address and no other.
4. Trade it for tokens
From your server (or, with PKCE and no secret, from your app):
RequestPOST
/api/v1/oauth2/token bash
curl https://moymo.net/api/v1/oauth2/token \
-d grant_type=authorization_code \
-d code=Qp2hY... \
-d redirect_uri=https://example.com/callback \
-d client_id=0f6c1a52-3b9e-4c1c-9d1b-6e7d2a8f4b10 \
-d client_secret=$MOYMO_CLIENT_SECRET \
-d code_verifier=$VERIFIERjs
const response = await fetch('https://moymo.net/api/v1/oauth2/token', {
method: 'POST',
body: new URLSearchParams({
grant_type: 'authorization_code',
code,
redirect_uri: 'https://example.com/callback',
client_id: '0f6c1a52-3b9e-4c1c-9d1b-6e7d2a8f4b10',
client_secret: process.env.MOYMO_CLIENT_SECRET,
code_verifier: verifier
})
})
const tokens = await response.json()python
import os
import requests
response = requests.post(
"https://moymo.net/api/v1/oauth2/token",
data={
"grant_type": "authorization_code",
"code": code,
"redirect_uri": "https://example.com/callback",
"client_id": "0f6c1a52-3b9e-4c1c-9d1b-6e7d2a8f4b10",
"client_secret": os.environ["MOYMO_CLIENT_SECRET"],
"code_verifier": verifier,
},
)
tokens = response.json()Either the client secret or the PKCE verifier has to prove the exchange; a code that was issued with a challenge needs the matching verifier, and a code issued without one needs the secret. You can send credentials in the body as above, or as HTTP Basic (client_id:client_secret, each form-encoded).
Response200OKJSON
json
{
"access_token": "moymo_at_9x3...",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "moymo_rt_Hk8...",
"scope": "identify servers"
}5. Call the API
RequestGET
/users/@me Shellbash
curl https://moymo.net/api/app/v1/users/@me \
-H "Authorization: Bearer moymo_at_9x3..."6. Refresh
An access token lasts 1 hour. When it expires, trade the refresh token for a new pair:
RequestPOST
/api/v1/oauth2/token Shellbash
curl https://moymo.net/api/v1/oauth2/token \
-d grant_type=refresh_token \
-d refresh_token=moymo_rt_Hk8... \
-d client_id=0f6c1a52-3b9e-4c1c-9d1b-6e7d2a8f4b10 \
-d client_secret=$MOYMO_CLIENT_SECRETEach refresh token works once: the answer carries the next one, and the old one stops working. Keep the newest. A refresh token lasts 30 days.
PKCE
Proof Key for Code Exchange makes a stolen code worthless. Before you send the member away, make a random verifier and keep it; send only its hash, the challenge. When you trade the code, send the verifier; Moymo hashes it and compares.
Making a verifier and its challenge
js
import { createHash, randomBytes } from 'node:crypto'
const verifier = randomBytes(48).toString('base64url') // 64 characters
const challenge = createHash('sha256').update(verifier).digest('base64url') // 43 characters
// send `challenge` (with code_challenge_method=S256) now; keep `verifier` until the code comes backpython
import base64
import hashlib
import secrets
verifier = secrets.token_urlsafe(48) # 64 characters
digest = hashlib.sha256(verifier.encode("ascii")).digest()
challenge = base64.urlsafe_b64encode(digest).rstrip(b"=").decode("ascii") # 43 characters
# send `challenge` (with code_challenge_method=S256) now; keep `verifier` until the code comes backThe verifier is 43 to 128 characters from A-Z a-z 0-9 - . _ ~. Only S256 is accepted: plain would send the verifier in the clear in the first request, which is exactly what PKCE is there to avoid.
An application with no client secret has to use PKCE. An application with a secret should too: it costs nothing.
Revoking
Your app can revoke a token it holds with the revoke endpoint. A member can take back everything they allowed an app in Settings > Connected apps, and every token it made stops working at once.
If a code is used twice
A code that is used a second time is treated as stolen: Moymo refuses it and revokes the tokens the first use made. If you see invalid_grant on a code you only used once, something else used it first.
Errors
Errors at the token endpoint follow the standard: {"error": "invalid_grant", "error_description": "..."}. They are in the OAuth2 reference. Errors in the consent step reach your app as query parameters on the redirect (?error=access_denied&error_description=...&state=...), or, when the request is wrong in a way that makes the redirect address untrustworthy (an unknown application, an unregistered address), are shown to the member on Moymo's page and not sent anywhere.