The flow at a glance
- Discover the authorization server from the well-known metadata.
- Register your client once (Dynamic Client Registration) to get a client_id.
- Generate a PKCE verifier + challenge per authorization.
- Redirect the user to /oauth/authorize; they sign in and consent.
- Exchange the returned code at /oauth/token for an access + refresh token.
- Call the API; refresh when the access token expires; revoke on logout.
1. Discover the authorization server
Everything you need is in the RFC 8414 metadata document — endpoints, supported scopes, and the PKCE method. Read endpoints from it rather than hard-coding paths.
curl https://api.skedcast.com/.well-known/oauth-authorization-server2. Register your client (DCR)
Register once (RFC 7591) to get a client_id. SkedCast issues PUBLIC clients only (PKCE, no client secret). Keep the registration_access_token if you want to update or delete the registration later (RFC 7592).
curl -X POST https://api.skedcast.com/oauth/register \
-H "Content-Type: application/json" \
-d '{
"client_name": "My App",
"redirect_uris": ["https://myapp.com/oauth/callback"],
"scope": "posts.read posts.compose"
}'{
"client_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"client_id_issued_at": 1751299200,
"redirect_uris": ["https://myapp.com/oauth/callback"],
"grant_types": ["authorization_code", "refresh_token"],
"token_endpoint_auth_method": "none",
"scope": "posts.read posts.compose",
"registration_access_token": "reg_…",
"registration_client_uri": "https://api.skedcast.com/oauth/register/f47ac10b-…"
}3. Generate a PKCE pair
Per authorization, create a high-entropy code_verifier and its S256 code_challenge (RFC 7636). Keep the verifier private; put the challenge in the authorize URL.
import { randomBytes, createHash } from "node:crypto";
// code_verifier: 43-128 unreserved chars (RFC 7636 §4.1)
const codeVerifier = randomBytes(32).toString("base64url");
// code_challenge = base64url(SHA-256(verifier)) (RFC 7636 §4.2)
const codeChallenge = createHash("sha256")
.update(codeVerifier)
.digest("base64url");4. Send the user to authorize
Redirect the user's browser to /oauth/authorize with your client_id, scopes, a random state (CSRF), and the PKCE challenge. They sign in and consent; SkedCast redirects back to your redirect_uri with ?code=…&state=…. Verify state matches what you sent.
GET https://api.skedcast.com/oauth/authorize
?response_type=code
&client_id=f47ac10b-…
&redirect_uri=https://myapp.com/oauth/callback
&scope=posts.read%20posts.compose
&state=<random-csrf-token>
&code_challenge=<codeChallenge>
&code_challenge_method=S2565. Exchange the code for tokens
POST the code back with your code_verifier. SkedCast verifies the PKCE challenge and returns an access + refresh token.
curl -X POST https://api.skedcast.com/oauth/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d grant_type=authorization_code \
-d code=<authorization_code> \
-d redirect_uri=https://myapp.com/oauth/callback \
-d client_id=f47ac10b-… \
-d code_verifier=<codeVerifier>{
"access_token": "eyJ…",
"token_type": "Bearer",
"expires_in": 900,
"scope": "posts.read posts.compose",
"refresh_token": "eyJ…"
}6. Call the API
Send the access token as a Bearer credential — exactly like an API key. Its power is still scopes ∩ the user's role.
curl https://api.skedcast.com/v1/agency \
-H "Authorization: Bearer eyJ…"7. Refresh when it expires
Access tokens last 15 minutes. Exchange your refresh token for a new pair before, or when a call returns 401.
curl -X POST https://api.skedcast.com/oauth/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d grant_type=refresh_token \
-d refresh_token=eyJ…8. Revoke on logout
When the user disconnects, revoke the refresh token (RFC 7009) — this kills the whole rotation family. The endpoint always returns 200, even for an unknown token, so it never leaks token state.
curl -X POST https://api.skedcast.com/oauth/revoke \
-H "Content-Type: application/x-www-form-urlencoded" \
-d token=eyJ…FAQ
- Do I need a client secret?
- No. SkedCast registers public clients only and requires PKCE (S256), so there is no client secret to store — the code_verifier proves possession at the token step.
- What is the resource parameter for?
- OAuth access tokens are audience-bound (RFC 8707). The optional resource parameter on /oauth/authorize and /oauth/token defaults to the SkedCast resource, so you can omit it; the metadata document lists the value if you want to set it explicitly.