Documentation
For app developers
myindieauth.com is a standard IndieAuth server. Any client that follows the current specification works with it; this page summarizes what that means.
If you only need to know who someone is, the simplest route is indielogin.com, which handles all of the steps below for you.
1. Discover the server
Fetch the URL the user entered (following redirects) and look for rel="indieauth-metadata" in an HTTP Link header or an HTML <link> element. Fetch that URL to get the server metadata:
{
"issuer": "https://dev.myindieauth.com/",
"authorization_endpoint": "https://dev.myindieauth.com/auth",
"token_endpoint": "https://dev.myindieauth.com/token",
"introspection_endpoint": "https://dev.myindieauth.com/introspect",
"revocation_endpoint": "https://dev.myindieauth.com/revoke",
"userinfo_endpoint": "https://dev.myindieauth.com/userinfo",
"code_challenge_methods_supported": ["S256"],
"authorization_response_iss_parameter_supported": true,
...
}
Each account on this server has its own metadata URL, but the documents are identical: every account shares one issuer and one set of endpoints.
2. Publish client metadata
Your client_id is a URL. Serve a JSON document there so the consent screen can show your app's name and logo:
{
"client_id": "https://app.example.com/",
"client_name": "Example App",
"client_uri": "https://app.example.com/",
"logo_uri": "https://app.example.com/logo.png",
"redirect_uris": ["https://app.example.com/callback"]
}
A redirect_uri on a different scheme, host or port from the client_id is only accepted if it is listed in redirect_uris. Apps without a metadata document still work, but the user only sees the bare URL. The older h-app microformat is not read.
3. Send the user to the authorization endpoint
https://dev.myindieauth.com/auth?response_type=code
&client_id=https://app.example.com/
&redirect_uri=https://app.example.com/callback
&state=RANDOM_STATE
&code_challenge=BASE64URL(SHA256(VERIFIER))
&code_challenge_method=S256
&scope=profile create
&me=https://user.example.net/
stateand PKCE withS256are required.scopeis optional. Leave it out if you only need to authenticate the user.meis optional but recommended: it tells the server which of the account's websites the user is signing in as.
The user returns to your redirect_uri with code, state and iss. Check that state matches and iss equals the issuer from the metadata.
4. Exchange the code
To get an access token, POST to the token endpoint within 60 seconds:
curl https://dev.myindieauth.com/token \
-d grant_type=authorization_code \
-d code=CODE \
-d client_id=https://app.example.com/ \
-d redirect_uri=https://app.example.com/callback \
-d code_verifier=VERIFIER
{
"access_token": "…",
"token_type": "Bearer",
"scope": "profile create",
"expires_in": 604800,
"refresh_token": "…",
"me": "https://user.example.net/",
"profile": { "name": "…", "url": "https://user.example.net/", "photo": "…" }
}
If no scope was granted, no access token is issued and only me is returned. If you only need to know who signed in, POST the same parameters to the authorization endpoint instead, which always returns just me (and profile if granted).
The user may choose a token that never expires; in that case there is no expires_in or refresh_token.
5. Verify the returned me
If the returned me differs from what the user entered, fetch it and confirm it declares the same authorization server before trusting it. This server only returns websites whose home page currently links to the signed-in account.
Refreshing and revoking
curl https://dev.myindieauth.com/token \
-d grant_type=refresh_token \
-d refresh_token=REFRESH_TOKEN \
-d client_id=https://app.example.com/
Refresh tokens rotate: each use returns a new one, and presenting an old one again revokes the grant. They expire after 90 days without use.
To sign out, POST token=… to https://dev.myindieauth.com/revoke. This revokes the access token and every token issued with it.
Userinfo
With a token that has the profile or email scope, GET https://dev.myindieauth.com/userinfo with an Authorization: Bearer header to fetch the current profile.