Skip to main content

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

PlatformRedirect lands in…How fe_code is delivered
Mobile web (browser)The same browser that started the flowHttpOnly 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 LinksNo 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

CodeTransportPurpose
fe_codeHttpOnly cookie (set by your backend at /prepare time)Proves the requesting browser is the same one that started the flow
agg_codeURL 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 a StorageEvent signal 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

StatusMeaningAction
403Binding cookie missing or code mismatchRestart the flow from /prepare — the session cannot be recovered
500Server errorSafe to retry the same request