Developers
Menu

How people give your app access, using the OAuth 2.0 authorization code flow.

We use the standard OAuth 2.0 authorization code flow, with PKCE for apps that run in a browser or on a phone. Most OAuth libraries support this out of the box, so you probably won't need to build it yourself.

EndpointLink
Authorizehttps://streetartcities.com/api/oauth/authorize
Tokenhttps://streetartcities.com/api/oauth/token
Revokehttps://streetartcities.com/api/oauth/revoke

Overview

 Your app                 Street Art Cities                    Person
    │                             │                               │
    │── 1. send to /authorize ───▶│                               │
    │                             │── 2. log in, allow access? ──▶│
    │                             │◀──────────── yes ─────────────│
    │◀── 3. redirect with code ───│                               │
    │── 4. POST /token ──────────▶│                               │
    │◀── access + refresh token ──│                               │
    │── 5. call the API ─────────▶│                               │

1. Send people to the authorize page

Send people's browser to the authorize link with these query parameters:

ParameterRequiredValue
response_typeYesAlways code
client_idYesYour app's client ID
redirect_uriYesOne of your app's redirect links, exactly as you registered it
scopeYesOne or more scopes, separated by spaces
stateRecommendedA random value you check when people come back
code_challengePublic appsYour PKCE code challenge
code_challenge_methodPublic appsAlways S256

We show a consent page with your app's name and description, and what it'll be able to do. People who aren't logged in are asked to log in first.

2. Handle the redirect

When someone allows access, we send them to your redirect link with a code and your state:

https://example.com/callback?code=3kW9...&state=abc123

Check that state matches the one you sent. If they said no, or something was wrong with the request, you get an error instead:

https://example.com/callback?error=access_denied&state=abc123
ErrorMeaning
access_deniedThe person didn't allow access
invalid_scopeNo scopes, or a scope we don't know
invalid_requestPKCE is missing for an app without a client secret, or the code challenge isn't valid
unsupported_response_typeresponse_type wasn't code
Note

If the client ID is unknown or the redirect link isn't registered, we can't safely send people back to you. They see an error page on Street Art Cities instead.

3. Swap the code for tokens

POST to the token link. You can send the body as JSON or as a form (application/x-www-form-urlencoded).

FieldValue
grant_typeauthorization_code
codeThe code from the redirect
redirect_uriThe same redirect link you used in step 1
code_verifierThe PKCE code verifier, if you used PKCE
client_idYour client ID
client_secretYour client secret, only for server apps

Server apps can also send their client ID and secret using HTTP Basic auth instead of in the body.

{
  "access_token": "eyJhbGciOi...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "Yh1c...",
  "scope": "edits:read edits:write"
}

Codes work once, and only for 10 minutes. See tokens for what to do when the access token runs out.

PKCE

Apps that run in a browser, on a phone or on someone's computer can't keep a client secret safe, so they use PKCE instead. It stops someone who intercepts the code from using it. Server apps can use it too, on top of their client secret, but don't have to.

Before step 1:

  1. Make a random code verifier: 43 to 128 characters, using A-Z, a-z, 0-9 and -._~.
  2. Make the code challenge: the SHA-256 hash of the verifier, encoded as base64url without padding.

Send the challenge in step 1 and the verifier in step 3. In JavaScript:

const verifier = base64url(crypto.getRandomValues(new Uint8Array(32)));
const challenge = base64url(
  new Uint8Array(
    await crypto.subtle.digest("SHA-256", new TextEncoder().encode(verifier)),
  ),
);
 
function base64url(bytes) {
  return btoa(String.fromCharCode(...bytes))
    .replace(/\+/g, "-")
    .replace(/\//g, "_")
    .replace(/=+$/, "");
}