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
POST /auth/resolve with the verified email → gets Ocean Key
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
- Go to Google Cloud Console → APIs & Services → Credentials → Create OAuth 2.0 Client ID
- Set application type to Web application
- Add your redirect URI (e.g.
https://yourapp.com/auth/google/callback) - 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
X-Ocean-Key(your developer key) is required — anonymous calls are rejected- Rate limited: 20 resolves per key per hour
- Email is normalized (trim + lowercase) — "Alice@Gmail.com" and "alice@gmail.com" resolve to the same user
- New users get a starting balance automatically
- The same email always resolves to the same Ocean Key, whether it was created via magic link or OAuth
- HTTPS enforced by InfiniteOcean
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.