JavaScript Callbacks
Overview
This guide shows you how to implement social login using JavaScript callbacks with the Median JavaScript Bridge. This approach keeps everything in the browser and works well for single-page apps and client-side authentication flows.
How it works
JavaScript callbacks let you access authentication tokens directly in your webpage's JavaScript code, without URL redirects or server-side token validation.
Here's the flow:
- A user clicks your login button, triggering the Median JavaScript Bridge
- The native social login UI appears (Facebook, Google, or Apple)
- After the user authenticates (or cancels), your JavaScript callback function runs
- The callback receives an object with the authentication token and user details
- You can immediately process this data client-side or send it to your backend
Before you start
You'll need:
- The ability to process authentication tokens through JavaScript
- Your social login providers already configured (Facebook, Google, or Apple)
Understanding the parameters
Each social login method accepts these parameters:
callback (required) — The JavaScript function that runs after login completes. It receives an object with the authentication token and user details.
scope (optional) — Defines what user data your app can access. Each provider has different scope options:
forceLimitedLogin (Facebook only) — Set to true to use Limited Login mode even when App Tracking Transparency is enabled.
nonce (Facebook only) — A string value used to verify the authenticity of the login when using Limited Login mode.
median.socialLogin.facebook.login({ 'callback' : <function>, 'scope' : '<text>', forceLimitedLogin: true | false, nonce: '<text>' });
median.socialLogin.google.login({ 'callback' : <function> });
median.socialLogin.apple.login({ 'callback' : <function>, 'scope' : '<text>' });Provider-specific implementation
The following sections provide complete implementation details for each social login provider. Each section includes response objects, platform-specific requirements (where applicable), and a working code example. You can implement one or more providers based on your needs.
Response objects
Here's what your callback function receives for Facebook.
Success response
{
"accessToken": "token string",
"userId": "1234567890",
"type": "facebook",
"userDetails": {
"userID": "<string>",
"name": "<string>",
"email": "<string>",
"imageURL": "<string>",
"friendIDs": "<string>",
"birthday": "<string>",
"ageRangeMin": "<number>",
"ageRangeMax": "<number>",
"hometown": "<string>",
"location": "<string>",
"gender": "<string>"
},
"authToken": "<limited-login-token>",
"nonce": "<text>",
"limitedLogin": true | false
}Note: Keys in userDetails may be unavailable or null based on the scope you requested.
Error response
{
"error": "error description",
"type": "facebook"
}Platform-specific requirements
Facebook on iOS
When users decline App Tracking Transparency (ATT), or tracking isn't enabled, Facebook uses "Limited Login" mode. You can also force this mode by setting forceLimitedLogin: true.
What changes in Limited Login mode:
- The accessToken won't be returned
- You'll get an authToken (a JWT) instead — this can't be used for Graph API calls
- A nonce value is available to verify login authenticity
- The limitedLogin key will be set to true
Read Facebook's documentation on validating Limited Login tokens
Implementation
Use the Median JavaScript Bridge method to trigger native login from your website. When a user clicks the button, the native login flow starts. After they authenticate, your callback function runs with the response object.
Here's an example:
<button class="facebook-login browser-only" onclick="facebookLogin()">
Log In With Facebook
</button>
<button class="facebook-login native-only"
onclick="median.socialLogin.facebook.login({ 'callback' : facebookLoginCallback, 'scope' : 'public_profile, email' });">
Log in With Facebook
</button>
<script>
const isMedian = navigator.userAgent.indexOf("median") >= 0;
if (isMedian) {
// Remove browser-only buttons
const elements = document.getElementsByClassName("browser-only")
while(elements.length > 0) {
elements[0].parentNode.removeChild(elements[0]);
}
} else {
// Remove native-only buttons
const elements = document.getElementsByClassName("native-only")
while(elements.length > 0) {
elements[0].parentNode.removeChild(elements[0]);
}
}
function facebookLoginCallback(response) {
console.log("Facebook Login Callback");
let accessToken;
if (response.status === "connected") {
// browser-only
accessToken = response.authResponse.accessToken;
} else if (response.type === "facebook") {
// native-only
accessToken = response.accessToken;
}
if (accessToken) {
FB.api("/me", "get", { fields: "id, email, first_name, last_name", access_token: accessToken }, function(response) {
const { id, email, first_name, last_name } = response;
const payload = {
email,
first_name,
last_name,
provider_type: "facebook",
provider_token: accessToken,
provider_uid: id,
}
// Call your backend's register endpoint
fetch("users/register", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(payload),
}).then(
(data) => data.json()
).then((data) => {
console.log("User is registered.");
}).catch((error) => {
console.error(error);
});
})
} else {
console.log("User cancelled login or did not fully authorize.");
}
}
function facebookLogin() {
FB.login(function(response) {
facebookLoginCallback(response);
}, {
scope: "email, public_profile",
});
}
window.fbAsyncInit = function() {
FB.init({
appId : "INSERT_FACEBOOK_APP_ID",
autoLogAppEvents : true,
xfbml : true,
version : "v12.0"
});
};
</script>
<script async defer crossorigin="anonymous" src="https://connect.facebook.net/en_US/sdk.js"></script>In this example, clicking the button activates the Median JavaScript Bridge and launches Facebook's native login. After the user authenticates, facebookLoginCallback() runs with an object containing the user's token. If they cancel, you'll get an error message instead.
Once you have the token, you can use it to fetch the user's Facebook profile. Then pass this data to your backend for processing.
Updated about 10 hours ago