Device Binding
Device binding is a mandatory security mechanism for the Link Protocol (carrier OAuth redirect). It cryptographically proves that the device which initiated authentication is the same device that received the carrier's redirect response. There is no opt-out — every Link Protocol session uses device binding automatically.
How It Works on Different Platforms
| Platform | Redirect lands in… | How fe_code is delivered |
|---|---|---|
| Mobile web (browser) | The same browser that started the flow | HttpOnly cookie (_glide_bind_{prefix}) — attaches automatically because the Web SDK uses credentials: 'include' |
| Native mobile (React Native / Android) | The native app via Universal Links / App Links | No browser cookie — the native SDK manages binding internally via the deep link callback |
On mobile web, the redirect opens a new tab in the same browser context, so cookies set during /prepare are available on the completion page. On native mobile, the carrier redirect triggers a deep link back into your app — the native SDK handles extracting the codes without relying on cookies.
The Two Codes
| Code | Transport | Purpose |
|---|---|---|
fe_code | HttpOnly cookie (set by your backend at /prepare time) | Proves the requesting browser is the same one that started the flow |
agg_code | URL fragment on the carrier redirect (#agg_code=X) | Proves the device actually received the carrier's redirect |
Both codes are validated server-side during /complete and again during /process (verify/get). If either is missing or invalid, the request fails with 403.
Flow Diagram
Backend Integration
Your backend uses the SDK's device-binding helpers at three points in the flow.
Node.js
import {
MagicalAuthClient,
buildSetBindingCookieHeader,
parseBindingCookie,
buildClearBindingCookieHeader,
getCompletionPageHtml,
} from '@glideidentity/glide-be-node-magical-auth';
const client = new MagicalAuthClient({ clientId, clientSecret, baseUrl });
// --- /prepare ---
const result = await client.prepare(request);
if (result.feCode) {
res.setHeader('Set-Cookie',
buildSetBindingCookieHeader(result.feCode, result.session.session_key, { secure: true }));
}
// --- /complete ---
const feCode = parseBindingCookie(req.headers.cookie, body.session_key);
if (!feCode) { res.status(403).json({ code: 'MISSING_BINDING_COOKIE' }); return; }
const completeResult = await client.complete({ ...body, fe_code: feCode });
// --- /process (verify or get-phone-number) ---
const feCode = parseBindingCookie(req.headers.cookie, body.session.session_key);
const result = await client.verifyPhoneNumber({ ...body, fe_code: feCode });
res.setHeader('Set-Cookie',
buildClearBindingCookieHeader(body.session.session_key, { secure: true }));
Java
import com.glideidentity.magicalauth.MagicalAuthClient;
import com.glideidentity.magicalauth.DeviceBinding;
MagicalAuthClient client = MagicalAuthClient.builder()
.clientId(clientId).clientSecret(clientSecret).baseUrl(baseUrl).build();
// --- /prepare ---
PrepareResult result = client.prepare(request);
if (result.getFeCode() != null) {
String cookie = DeviceBinding.buildSetBindingCookieHeader(
result.getFeCode(), result.getSession().getSessionKey(),
BindingCookieOptions.builder().secure(true).build());
exchange.getResponseHeaders().add("Set-Cookie", cookie);
}
// --- /complete ---
String feCode = DeviceBinding.parseBindingCookie(cookieHeader, sessionKey);
if (feCode == null) { sendError(exchange, 403, "MISSING_BINDING_COOKIE"); return; }
CompleteResponse response = client.complete(
CompleteRequest.builder().sessionKey(sessionKey).feCode(feCode).aggCode(aggCode).build());
// --- /process (verify or get-phone-number) ---
String feCode = DeviceBinding.parseBindingCookie(cookieHeader, sessionKey);
VerifyPhoneNumberResponse response = client.verifyPhoneNumber(
VerifyPhoneNumberRequest.builder().session(session).credential(credential).feCode(feCode).build());
DeviceBinding.buildClearBindingCookieHeader(sessionKey, cookieOptions);
Frontend SDKs
The frontend SDKs handle device binding automatically:
- Web SDK — sends all requests with
credentials: 'include'so the binding cookie attaches. Listens for aStorageEventsignal from the completion tab to know when to call/process. - React Native SDK / Android SDK — handles the deep link callback from Universal Links / App Links and manages binding codes internally. No cookie management needed on the client side.
Completion Page
Your backend must serve a completion page at the URL you configured as the redirect target. The SDK provides a helper that generates the HTML — it extracts agg_code from the URL fragment and POSTs it to your /complete endpoint:
// Node.js
app.get('/glide-complete', (req, res) => {
res.type('html').send(getCompletionPageHtml('/api/magical-auth/complete'));
});
// Java
String html = DeviceBinding.getCompletionPageHtml("/api/magical-auth/complete");
Error Handling
| Status | Meaning | Action |
|---|---|---|
403 | Binding cookie missing or code mismatch | Restart the flow from /prepare — the session cannot be recovered |
500 | Server error | Safe to retry the same request |