Lewati ke konten utama

Integrasi Node.js Backend

Recipe ini menunjukkan cara backend Node.js melakukan sign in pengguna dengan Muhajir Studio, menyimpan OAuth state di sisi server, dan memanggil external API dari kode server yang tepercaya.

Kapan Menggunakan Pola Ini

Gunakan pola ini ketika aplikasi Anda memiliki backend yang dapat mempertahankan HTTP-only application session. Browser hanya menerima session cookie milik aplikasi Anda; OIDC tokens tetap berada di server Anda.

ServiceURL ProduksiDigunakan untuk
APIhttps://api.muhajirstudio.com/Authorization, token exchange, refresh, dan /api/external/*.
Main webhttps://login.muhajirstudio.com/Login pengguna, signup, email verification, dan consent.
Admin webhttps://admin.muhajirstudio.com/Application registration.

Setup Admin

Minta admin mendaftarkan aplikasi Anda di web-admin dengan:

FieldContohCatatan
Allowed web originhttps://app.example.comOrigin saja; tanpa path, query, fragment, atau wildcard.
Redirect path/auth/callbackPath saja.
Redirect URIhttps://app.example.com/auth/callbackDiturunkan dari origin + path.
Scopesopenid profile email offline_accessScope yang dipilih admin adalah source of truth.

Applications yang dibuat admin adalah Public PKCE clients. Aplikasi menerima client_id dan tidak menerima client_secret.

1. Mulai Login

Buat route login di aplikasi Anda yang menghasilkan nilai PKCE dan CSRF, menyimpannya di server session, lalu mengarahkan browser ke Muhajir Studio.

Pada contoh di bawah, apiBaseUrl adalah production API URL dan clientId adalah public client ID dari web-admin.

app.get('/login', async (req, res) => {
const codeVerifier = randomUrlSafeString();
const codeChallenge = await sha256Base64Url(codeVerifier);
const state = randomUrlSafeString();
const nonce = randomUrlSafeString();

req.session.oidc = { codeVerifier, state, nonce };

const params = new URLSearchParams({
client_id: clientId,
redirect_uri: 'https://app.example.com/auth/callback',
response_type: 'code',
scope: 'openid profile email offline_access',
state,
nonce,
code_challenge: codeChallenge,
code_challenge_method: 'S256',
});

res.redirect(`${apiBaseUrl}/api/auth/oauth2/authorize?${params}`);
});

code_challenge_method harus S256. Plain PKCE challenges tidak diterima.

2. Tangani Callback

Setelah pengguna sign in, melakukan email verification jika diperlukan, dan memberi consent, Muhajir Studio mengarahkan kembali ke callback terdaftar dengan code dan state.

Callback Anda harus memverifikasi state yang kembali sebelum token exchange.

app.get('/auth/callback', async (req, res) => {
if (req.query.state !== req.session.oidc?.state) {
res.status(400).send('Invalid state');
return;
}

const tokenRes = await fetch(`${apiBaseUrl}/api/auth/oauth2/token`, {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
grant_type: 'authorization_code',
code: String(req.query.code),
code_verifier: req.session.oidc.codeVerifier,
client_id: clientId,
redirect_uri: 'https://app.example.com/auth/callback',
}),
});

const tokenBody = await tokenRes.json();
if (!tokenRes.ok) {
res.status(400).json(tokenBody);
return;
}

// Simpan tokens di sisi server, terkait dengan application session Anda.
req.session.tokens = tokenBody;
delete req.session.oidc;
res.redirect('/');
});

Jangan kirim client_secret untuk Public PKCE clients yang dibuat admin.

3. Panggil External API

Gunakan access token dari server Anda untuk memanggil stable external endpoints.

const meRes = await fetch(`${apiBaseUrl}/api/external/me`, {
headers: { Authorization: `Bearer ${accessToken}` },
});

if (meRes.status === 401) {
// Token hilang, expired, unknown, atau tidak lagi valid.
}

if (meRes.status === 403) {
// Token valid tetapi tidak memiliki required scope seperti openid.
}

GET /api/external/me selalu membutuhkan openid. Field tambahan hanya dikembalikan ketika token memiliki scope yang sesuai seperti profile, email, atau permissions.

4. Refresh Tokens

Jika token response menyertakan refresh_token, simpan secara terenkripsi atau di protected server-side session store. Gunakan hanya dari backend Anda.

POST /api/auth/oauth2/token
Content-Type: application/x-www-form-urlencoded

grant_type=refresh_token&refresh_token=REFRESH_TOKEN&client_id=CLIENT_ID

Jika refresh gagal dengan invalid_grant, hapus tokens yang tersimpan dan arahkan pengguna melalui login lagi.

Checklist Produksi

  • Simpan state, nonce, dan code_verifier di sisi server per login attempt.
  • Validasi state sebelum menukar code.
  • Jangan pernah mengirim client_secret untuk Public PKCE clients.
  • Simpan tokens hanya di server, bukan di storage yang dapat diakses browser.
  • Daftarkan production origin dan callback path yang tepat.
  • Tangani OAuth callback errors seperti error=access_denied.
  • Perlakukan 401 sebagai perlu re-authentication dan 403 sebagai missing scope atau insufficient consent.