Social Login with OAuth

Add "Sign in with Google" or "Sign in with GitHub" to your app using POST /auth/resolve. Your backend verifies the email via any OAuth provider, then resolves it to an Ocean Key — one API call.

1 How It Works

1 User clicks "Sign in with Google" redirects to Google consent screen
2 Google redirects back to your server with an auth code
3 Your server exchanges code for access token gets email from Google
4 Your server calls POST /auth/resolve with the verified email gets Ocean Key
5 Frontend stores the Ocean Key user is logged in

InfiniteOcean doesn't need to know about Google or GitHub. It just needs a verified email. Your backend is responsible for the OAuth flow and email verification — IO handles the rest. This is what makes it composable: it works with any email verification method. Google, GitHub, Apple, your own SMTP server — if you can confirm the email is real, POST /auth/resolve gives you a key.

2 Set Up Google OAuth

  1. Go to Google Cloud Console → APIs & Services → Credentials → Create OAuth 2.0 Client ID
  2. Set application type to Web application
  3. Add your redirect URI (e.g. https://yourapp.com/auth/google/callback)
  4. Copy the Client ID and Client Secret

3 Frontend — Redirect to Google

When the user clicks "Sign in with Google", redirect them to Google's consent screen:

const GOOGLE_CLIENT_ID = "your-client-id.apps.googleusercontent.com";
const REDIRECT_URI = "https://yourapp.com/auth/google/callback";

function loginWithGoogle() {
  const url = "https://accounts.google.com/o/oauth2/v2/auth?"
    + `client_id=${GOOGLE_CLIENT_ID}`
    + `&redirect_uri=${encodeURIComponent(REDIRECT_URI)}`
    + `&response_type=code`
    + `&scope=email`;
  window.location.href = url;
}

4 Backend — Exchange Code → Email → Ocean Key

This is the core. Your callback handler does three things: exchange the auth code for tokens, get the verified email, and resolve it to an Ocean Key:

async function handleGoogleCallback(code) {
  // Exchange the auth code for tokens
  const tokenRes = await fetch("https://oauth2.googleapis.com/token", {
    method: "POST",
    headers: { "Content-Type": "application/x-www-form-urlencoded" },
    body: new URLSearchParams({
      code,
      client_id: GOOGLE_CLIENT_ID,
      client_secret: GOOGLE_CLIENT_SECRET,
      redirect_uri: REDIRECT_URI,
      grant_type: "authorization_code",
    }),
  });
  const { access_token } = await tokenRes.json();

  // Get the user's verified email from Google
  const userRes = await fetch("https://www.googleapis.com/oauth2/v2/userinfo", {
    headers: { "Authorization": `Bearer ${access_token}` },
  });
  const { email } = await userRes.json();

  // Resolve the verified email to an Ocean Key — the only IO call
  const ioRes = await fetch("https://api.infiniteocean.io/auth/resolve", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "X-Ocean-Key": DEVELOPER_KEY,
    },
    body: JSON.stringify({ email }),
  });
  const { ocean_key, public_id, is_new } = await ioRes.json();

  return { ocean_key, public_id, is_new };
}

Response — new user (201)

{ "ocean_key": "VLolm5...", "public_id": "569ff7516208", "is_new": true }

Response — returning user (200)

{ "ocean_key": "VLolm5...", "public_id": "569ff7516208", "is_new": false }

The X-Ocean-Key header is YOUR developer key, not the user's key. You're responsible for verifying the email — IO trusts you to only resolve emails you've confirmed.

5 Frontend — Store the Key

After Google redirects back to your app with the auth code, exchange it via your backend and store the resulting Ocean Key:

// After redirect back from Google
const params = new URLSearchParams(location.search);
const code = params.get("code");

if (code) {
  // Clean the URL
  history.replaceState(null, "", location.pathname);

  // Call your backend to exchange code → Ocean Key
  const res = await fetch(`/auth/google/callback?code=${code}`);
  const { ocean_key } = await res.json();

  // Store it — used for all IO API calls
  localStorage.setItem("ocean_key", ocean_key);
}

// Use the key everywhere
const key = localStorage.getItem("ocean_key");
const data = await fetch("https://api.infiniteocean.io/entity/myapp/profile?key=me", {
  headers: { "X-Ocean-Key": key },
}).then(r => r.json());

6 GitHub OAuth (Same Pattern)

The pattern is identical: redirect to the provider, exchange the code for an access token, get the verified email, and call POST /auth/resolve. Here's the full GitHub version:

// Frontend — redirect to GitHub
const GITHUB_CLIENT_ID = "your-github-client-id";
window.location.href =
  `https://github.com/login/oauth/authorize?client_id=${GITHUB_CLIENT_ID}&scope=user:email`;

// Backend — exchange code → email → Ocean Key
async function handleGitHubCallback(code) {
  // Exchange code for access token
  const tokenRes = await fetch("https://github.com/login/oauth/access_token", {
    method: "POST",
    headers: { "Content-Type": "application/json", "Accept": "application/json" },
    body: JSON.stringify({
      client_id: GITHUB_CLIENT_ID,
      client_secret: GITHUB_CLIENT_SECRET,
      code,
    }),
  });
  const { access_token } = await tokenRes.json();

  // Get the user's primary verified email
  const emails = await (await fetch("https://api.github.com/user/emails", {
    headers: { "Authorization": `Bearer ${access_token}`, "User-Agent": "MyApp" },
  })).json();
  const email = emails.find(e => e.primary && e.verified)?.email;

  // Same single IO call
  const { ocean_key } = await (await fetch("https://api.infiniteocean.io/auth/resolve", {
    method: "POST",
    headers: { "Content-Type": "application/json", "X-Ocean-Key": DEVELOPER_KEY },
    body: JSON.stringify({ email }),
  })).json();

  return { ocean_key };
}

Apple, Microsoft, Discord, Slack — any OAuth provider that gives you a verified email works. The pattern is always: redirect → exchange → get email → POST /auth/resolve.

7 Security Notes

8 Magic Link + OAuth Together

Both methods are fully compatible. A user who first signs in via magic link and then via Google OAuth (same email) gets the same Ocean Key. There's no conflict — POST /auth/resolve and POST /auth/login use the same underlying user system.

This means you can offer a login form with multiple options — a magic link email field, a "Sign in with Google" button, a "Sign in with GitHub" button — and users always end up with the same key for the same email address.

No account merging needed. You can offer multiple login methods — magic link, Google, GitHub — and users always get the same Ocean Key for the same email. One identity, many entry points.

Once authenticated, use Drops for all data operations. After your user has an Ocean Key (via any auth method above), all data reads, writes, queries, and function calls can use Drops — a compact language that replaces curl commands with simple statements like write, read, query, and run.

← Magic Link Auth ← Back to Tutorials Next: Shared Components →