Skip to content

Native App Social Login

Native social login allows mobile app users to sign in using their existing Google, Facebook, or Apple accounts using platform-native SDKs.

┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ Your App │ │ Provider SDK │ │ Horizon API │
└────────┬────────┘ └────────┬─────────┘ └────────┬────────┘
│ │ │
│ 1. User taps │ │
│ "Sign in with X" │ │
│───────────────────────>│ │
│ │ │
│ 2. User authenticates │ │
│<───────────────────────│ │
│ (returns token) │ │
│ │ │
│ 3. appSocialLogin mutation │
│─────────────────────────────────────────────────>│
│ │ │
│ 4. Authentication response │
│ (Horizon token + customer info) │
│<─────────────────────────────────────────────────│

Query the API to discover which social login providers are enabled:

query GetSocialProviders {
socialLoginProviders {
code
name
colour
iconUrl
clientId
}
}

Example Response:

{
"data": {
"socialLoginProviders": [
{
"code": "google",
"name": "Continue with Google",
"colour": "#4285F4",
"iconUrl": "https://example.com/google-icon.svg",
"clientId": "123456789.apps.googleusercontent.com"
},
{
"code": "facebook",
"name": "Continue with Facebook",
"colour": "#1877F2",
"iconUrl": "https://example.com/facebook-icon.svg",
"clientId": "987654321"
}
]
}
}
Field Usage
code Use as providerCode in the login mutation
name Display text for your login button
colour Button background colour (hex)
iconUrl Provider logo for your button
clientId Configure your native SDK with this value
Provider Platform Token Field What to Send
Google iOS accessToken OAuth access token
Google Android authCode Server authorization code
Facebook Both accessToken OAuth access token
Apple Both accessToken Identity token (JWT)

Google Android Note: The Android SDK returns a server authorization code instead of an access token. Request the server auth code when configuring Google Sign-In and send it as authCode.

After the user authenticates with the provider SDK, call the appSocialLogin mutation:

mutation AppSocialLogin($input: AppBasedSocialLoginInput!) {
appSocialLogin(input: $input) {
authenticationResponse {
error
token
customer {
id
email
fullName
}
newCustomer
}
form {
title
fields {
name
label
required
type
}
}
socialLoginToken
socialLinkId
socialIdentity {
email
fullName
}
}
}

Google (iOS):

{
"input": {
"providerCode": "google",
"accessToken": "ya29.a0AfH6SMBx..."
}
}

Google (Android):

{
"input": {
"providerCode": "google",
"authCode": "4/P7q7W91a-oMsCeLvIaQm6bTrgtp7..."
}
}

Facebook:

{
"input": {
"providerCode": "facebook",
"accessToken": "EAAGm0PX4ZCps..."
}
}

Apple:

{
"input": {
"providerCode": "apple",
"accessToken": "eyJraWQiOiJXNldjT0..."
}
}

When error is null, login was successful:

{
"data": {
"appSocialLogin": {
"authenticationResponse": {
"error": null,
"token": "abc123def456...",
"customer": {
"id": "12345",
"email": "user@example.com",
"fullName": "John Doe"
},
"newCustomer": false
}
}
}
}

Store the token securely and use it for authenticated requests. If newCustomer is true, consider showing a welcome flow.

When the social provider doesn’t supply all required data:

{
"data": {
"appSocialLogin": {
"authenticationResponse": {
"error": "INVALID_DATA"
},
"form": {
"title": "Complete Your Profile",
"fields": [
{ "name": "email", "label": "Email Address", "required": true }
]
},
"socialLoginToken": "temp-token-for-retry...",
"socialIdentity": {
"email": null,
"fullName": "John Doe"
}
}
}
}

Display a form with the fields from form.fields, pre-fill data from socialIdentity, and submit using the socialLogin mutation with missingInformation:

mutation CompleteSocialLogin($input: SocialLoginInput!) {
socialLogin(input: $input) {
authenticationResponse {
error
token
customer { id email }
}
}
}

Variables:

{
"input": {
"socialAuthenticationToken": "temp-token-for-retry...",
"missingInformation": {
"email": "user@example.com"
}
}
}
Section titled “Account Linking Required (SOCIAL_LINK_PENDING)”

When an account with the same email already exists:

{
"data": {
"appSocialLogin": {
"authenticationResponse": {
"error": "SOCIAL_LINK_PENDING"
},
"socialLinkId": "link-abc123",
"socialIdentity": {
"email": "existing@example.com"
}
}
}
}

Send a verification email to confirm account ownership:

mutation RequestVerification {
requestSocialLinkVerificationEmail(input: {
socialAuthenticationToken: "the-social-token..."
}) {
error
socialAuthenticationToken
}
}
Error Code Description Resolution
INVALID_DATA Missing required data Display form, collect data, retry
SOCIAL_LINK_PENDING Email matches existing account Send verification email
INVALID_TOKEN Provider token is invalid Re-authenticate with provider
EXPIRED_TOKEN Provider token has expired Re-authenticate with provider
ACCOUNT_LOCKED Account is locked Direct user to password reset

Include the token in subsequent authenticated requests:

fetch('https://api.thehut.net/myprotein/en/graphql', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Opaque abc123def456...'
},
body: JSON.stringify({ query, variables })
});

Security Best Practices:

Do Don’t
Store in secure storage (Keychain, EncryptedSharedPreferences) Store in plain text
Clear on logout Log tokens to console
Transmit only over HTTPS Share tokens between apps

Logged-in users can view and manage connected social accounts:

query {
customer {
socialLinks(status: LINKED) {
socialLinkId
socialLoginProvider { code name }
username
status
}
}
}

Remove a social link:

mutation {
removeSocialLink(input: { socialLinkId: "link-id-123" })
}

Why does Google Android use authCode instead of accessToken?

Google’s Android SDK returns a server authorization code that gets exchanged server-side. This provides better security as the access token is never exposed on the device.

What if a provider isn’t in socialLoginProviders?

The provider isn’t enabled for your site. Contact your account manager to enable additional providers.

Can users link multiple social accounts?

Yes. A user can link Google, Facebook, and Apple to the same account and sign in with any of their linked providers.