Implement enterprise SSO with OAuth providers in Google Antigravity applications.
# OAuth SSO Patterns for Google Antigravity
Implement enterprise-grade Single Sign-On with OAuth 2.0 and social providers.
## OAuth Configuration
```typescript
// lib/supabase/auth.ts
import { createClient } from "@/lib/supabase/client";
export type OAuthProvider = "google" | "github" | "azure";
export async function signInWithOAuth(provider: OAuthProvider, redirectTo?: string) {
const supabase = createClient();
const { data, error } = await supabase.auth.signInWithOAuth({
provider,
options: {
redirectTo: redirectTo || `${window.location.origin}/auth/callback`,
scopes: provider === "google" ? "openid email profile" : undefined,
},
});
if (error) throw error;
return data;
}
```
## Callback Handler
```typescript
// app/auth/callback/route.ts
import { NextRequest, NextResponse } from "next/server";
import { createClient } from "@/lib/supabase/server";
export async function GET(request: NextRequest) {
const { searchParams, origin } = new URL(request.url);
const code = searchParams.get("code");
const next = searchParams.get("next") || "/";
if (code) {
const supabase = createClient();
const { data, error } = await supabase.auth.exchangeCodeForSession(code);
if (error) {
return NextResponse.redirect(`${origin}/auth/error?message=${encodeURIComponent(error.message)}`);
}
if (data.user) {
const { data: existing } = await supabase.from("profiles").select("id").eq("id", data.user.id).single();
if (!existing) {
await supabase.from("profiles").insert({
id: data.user.id,
email: data.user.email,
full_name: data.user.user_metadata.full_name || data.user.user_metadata.name,
avatar_url: data.user.user_metadata.avatar_url,
});
}
}
return NextResponse.redirect(`${origin}${next}`);
}
return NextResponse.redirect(`${origin}/auth/error`);
}
```
## Social Login Buttons
```typescript
// components/auth/SocialLoginButtons.tsx
"use client";
import { useState } from "react";
import { signInWithOAuth, OAuthProvider } from "@/lib/supabase/auth";
const providers: { id: OAuthProvider; name: string; icon: string; color: string }[] = [
{ id: "google", name: "Google", icon: "G", color: "#4285f4" },
{ id: "github", name: "GitHub", icon: "GH", color: "#333" },
{ id: "azure", name: "Microsoft", icon: "M", color: "#00a4ef" },
];
export function SocialLoginButtons({ redirectTo }: { redirectTo?: string }) {
const [loading, setLoading] = useState<OAuthProvider | null>(null);
const handleLogin = async (provider: OAuthProvider) => {
setLoading(provider);
try {
await signInWithOAuth(provider, redirectTo);
} catch (error) {
console.error(error);
setLoading(null);
}
};
return (
<div className="social-buttons">
{providers.map((p) => (
<button key={p.id} onClick={() => handleLogin(p.id)} disabled={loading !== null} style={{ backgroundColor: p.color }}>
<span>{p.icon}</span>
<span>{loading === p.id ? "Connecting..." : `Continue with ${p.name}`}</span>
</button>
))}
</div>
);
}
```
## Enterprise SSO Check
```typescript
// lib/sso/enterprise.ts
import { createClient } from "@/lib/supabase/server";
export async function getSSOProviderForDomain(domain: string) {
const supabase = createClient();
const { data } = await supabase.from("sso_providers").select("*").contains("domains", [domain]).eq("enabled", true).single();
return data;
}
```
## Enterprise Login Component
```typescript
// components/auth/EnterpriseLogin.tsx
"use client";
import { useState } from "react";
import { getSSOProviderForDomain } from "@/lib/sso/enterprise";
export function EnterpriseLogin() {
const [email, setEmail] = useState("");
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setLoading(true);
setError(null);
try {
const domain = email.split("@")[1];
const ssoProvider = await getSSOProviderForDomain(domain);
if (ssoProvider) {
const returnUrl = encodeURIComponent(`${window.location.origin}/auth/sso/callback`);
window.location.href = `${ssoProvider.sso_url}?RelayState=${returnUrl}`;
} else {
setError("No SSO configured for this domain.");
}
} catch (err) {
setError("Failed to check SSO configuration");
} finally {
setLoading(false);
}
};
return (
<form onSubmit={handleSubmit}>
<h2>Enterprise Login</h2>
<input type="email" value={email} onChange={(e) => setEmail(e.target.value)} placeholder="Work email" required />
<button type="submit" disabled={loading}>{loading ? "Checking..." : "Continue"}</button>
{error && <p className="error">{error}</p>}
</form>
);
}
```
## Best Practices
1. **State Parameter**: Use state parameter to prevent CSRF
2. **PKCE Flow**: Use PKCE for public clients
3. **Token Storage**: Store tokens securely in httpOnly cookies
4. **Session Timeout**: Implement proper session management
5. **Audit Logging**: Log all authentication eventsThis sso prompt is ideal for developers working on:
By using this prompt, you can save hours of manual coding and ensure best practices are followed from the start. It's particularly valuable for teams looking to maintain consistency across their sso implementations.
Yes! All prompts on Antigravity AI Directory are free to use for both personal and commercial projects. No attribution required, though it's always appreciated.
This prompt works excellently with Claude, ChatGPT, Cursor, GitHub Copilot, and other modern AI coding assistants. For best results, use models with large context windows.
You can modify the prompt by adding specific requirements, constraints, or preferences. For sso projects, consider mentioning your framework version, coding style, and any specific libraries you're using.