MAL authentication
MAL uses OAuth2 with PKCE. Register an application at the MAL API panel to get a client ID.
1. Build the authorization URL
import { buildMalAuthorizationUrl } from "anilink-api-wrapper";
const codeVerifier = createPkceVerifier(); // your PKCE verifier (43-128 chars)
const authorizeUrl = buildMalAuthorizationUrl("mal-client-id", codeChallenge, "csrf-state");
// Redirect the user to `authorizeUrl`.buildMalAuthorizationUrl(clientId, codeChallenge, state?) takes the S256 code challenge derived from your verifier. The optional state is CSRF protection — validate it on the redirect before exchanging the code.
2. Exchange the code
import { getMalAccessToken, AniLink } from "anilink-api-wrapper";
const token = await getMalAccessToken({
clientId: "mal-client-id",
code, // the `code` query parameter from the redirect
codeVerifier, // the original verifier
// clientSecret: "optional", // only for applications that use one
});
const aniLink = new AniLink({
mal: { accessToken: token.access_token, refreshToken: token.refresh_token, clientId: "mal-client-id" },
});Token requests use a default timeout of 10 seconds. Pass options on the request to override transport settings for the call.
3. Refresh before expiry
import { getMalTokenExpiry, refreshMalAccessToken } from "anilink-api-wrapper";
if (Date.now() >= getMalTokenExpiry(token).getTime() - 60_000) {
const refreshed = await refreshMalAccessToken({
clientId: "mal-client-id",
refreshToken: token.refresh_token,
});
token = { ...refreshed, refresh_token: refreshed.refresh_token ?? token.refresh_token };
}getMalTokenExpiry(response, now?) computes the absolute expiry from expires_in. The refresh response may omit refresh_token. Keep the stored one when it does (rotation semantics).
Constants and types
| Export | Value / shape |
|---|---|
MAL_API_BASE_URL | https://api.myanimelist.net/v2 |
MAL_AUTHORIZE_URL | https://myanimelist.net/v1/oauth2/authorize |
MAL_TOKEN_URL | The MAL OAuth2 token endpoint |
MAL_API_REFERENCE | Link to the MAL API v2 reference |
MalTokenResponse | { access_token, token_type, expires_in, refresh_token?, scope? } |
MalAuthorizationCodeRequest | { clientId, code, codeVerifier, clientSecret?, options? } |
MalRefreshTokenRequest | { clientId, refreshToken, clientSecret?, options? } |
Safe state validation
Generate a fresh random state per login attempt, store it server-side bound to the session, and compare with a timing-safe equality check before calling getMalAccessToken. Reject mismatches immediately.
Next steps
- MAL client configuration — storing the tokens.
- MAL operations — what the token unlocks.