Sign-in API
The contract a program is written against. If you are working on the MCCP app - the Moddable Console Capture Platform - this is the document you need; you do not need to read the site's source. (The repository still carries the old name in its address, which GitHub redirects; the name in these documents is the current one.)
Accounts belong to the site, not to your program. The same account signs in at
dasmaffin.com/auth/login in a browser and here from an application, and a person can bind more than
one way in to it. Your program never sees a password after the moment somebody types one.
Base address: https://dasmaffin.com. Everything below hangs off /api/auth.
1. Make an account
POST /api/auth/register
Content-Type: application/json
{ "email": "someone@example.com", "password": "a long enough one",
"displayName": "Someone", "client": "MCCP 1.4 on DESKTOP-1234" }
displayName is optional. client is optional and is worth sending: it is stored against the
refresh token, so a person can later recognise which machine is signed in.
201 with the same body as signing in, below. A letter goes to the address asking them to confirm
it, and the account is signed in at once either way - confirming is not currently required to use
it. If that changes you will see 403 on a later sign-in rather than a different answer here.
| Code | Meaning |
|---|---|
400 |
Not an email address, or a password shorter than 10 characters |
403 |
Registration is switched off |
409 |
That address already has an account |
429 |
Too many attempts from here; the body carries retryAfterSeconds |
2. Sign in
POST /api/auth/login
Content-Type: application/json
{ "email": "someone@example.com", "password": "a long enough one", "client": "MCCP 1.4",
"code": "123456" }
code is only wanted by accounts that have set up an authenticator app, and you do not have to
know in advance which those are: send the password on its own, and if a code is needed the refusal
says so. See below.
200:
{
"accessToken": "CfDJ8...",
"expiresInSeconds": 1800,
"refreshToken": "kQ7x...",
"accountId": 41,
"displayName": "Someone"
}
Store the refresh token, never the password. The refresh token is what keeps somebody signed in between runs; the password should not survive the form it was typed into.
| Code | Meaning |
|---|---|
401 |
No such address, or the wrong password. One answer for both, word for word - do not try to tell them apart, and do not word your own message as though you could |
401 with "twoFactorRequired": true |
The password was right and a code from their authenticator app is missing or wrong. Ask for the code and post the whole thing again, password included |
403 |
The address has not been confirmed (only possible once verification is switched on) |
429 |
Too many attempts; retryAfterSeconds says how long |
3. Use it
Authorization: Bearer <accessToken>
The access token is good for thirty minutes and cannot be withdrawn inside that window, which is why it is short. Do not try to decode it; treat it as opaque, because its contents are ours to change.
GET /api/auth/me
200 with accountId, kind (Steam or Password), subject, displayName, verified. 401
if the token has expired, was edited, or names a login that has since been deleted.
4. Stay signed in
POST /api/auth/refresh
Content-Type: application/json
{ "refreshToken": "kQ7x...", "client": "MCCP 1.4" }
200 with a fresh pair, exactly like signing in.
A refresh token works once. The one you sent is dead the moment this answers, and the answer carries its replacement - so store the new one before you do anything else with it. A crash between the two costs somebody a sign-in.
401 means expired, already used, or signed out. The only thing to do with it is ask for the
password again.
5. Forgotten password
POST /api/auth/forgot
Content-Type: application/json
{ "email": "someone@example.com" }
204, always. A link to choose a new password is emailed if there is an account with that address,
and the answer is the same when there is not - otherwise this endpoint is a way for anybody to find
out which addresses are registered here.
The reset itself happens on the website, because the link goes to a mailbox and is opened in a
browser. Your program's part is this call and a sentence telling them to go and read their mail.
Once they have, the old password stops working and every refresh token on the account is dead, so
expect a 401 on your next refresh and ask for the password again.
429 if it is asked too often, with retryAfterSeconds.
6. Sign out
POST /api/auth/logout
Content-Type: application/json
{ "refreshToken": "kQ7x..." }
204, always - including for a token that was never real, because answering differently would let
anybody ask this endpoint whether a token they found is genuine. The access token keeps working
until it expires; that is what a thirty-minute lifetime is for.
Things that will bite you if nobody says them
A person may have no password at all. Somebody who signed in through Steam on the website has an
account with a Steam login and nothing else, and /api/auth/login will never accept them. They add a
password at dasmaffin.com/auth/profile while signed in, and then both work and both lead to the same
accountId. If your program has a "sign in" screen and no way to explain that, people who already
have an account will be certain it is broken.
Handle twoFactorRequired or people with an authenticator app cannot sign in at all. It is the
one thing a 401 here tells you that a wrong password does not. Read it, show a field for six
digits, and post email, password and code together - there is no separate endpoint and nothing
is remembered between the two attempts, so the password has to come with it. A wrong password never
carries this flag, so it can never be used to find out who has a second factor.
A code works once. If somebody mistypes and you retry with the same digits, it will be refused even when it was right. Ask for the current ones.
accountId is the identity. Nothing else is. Not the email - a person can have several logins on
one account, and the address is one of them. Store the id.
A password reset ends in a browser, not in your program. The link is emailed and opened there,
because that is what proves the mailbox is theirs. POST /api/auth/forgot is the whole of your side
of it - after that, tell them to go and read their mail.
Everything is counted. Ten failures per address and per caller in fifteen minutes, and both
halves are counted at once. Retrying a wrong password in a loop locks the account out for everybody,
including whoever is typing it correctly on the website. Back off on a 429 and show the
retryAfterSeconds.
A password account holds no permissions here, and no module has to accept it. TGSC refuses them
outright: its pages answer 403 to a password session, because everything it knows about a person is
keyed on a SteamID64. That is a property of that module, not of the account.
The tokens are not JWTs. They are opaque strings signed by the site. Nothing in them is readable and nothing in them should be parsed.