The simplest possible SSO integration is two HTML tags. A script tag to load the SDK, and a link that points at the provider. No backend, no client secret, no OAuth app registration on your side. Here’s how it works.
The two tags
<script src="https://acme.loginwith.dev/cdn/sdk-latest" defer></script>
<a href="https://google.loginwith.link">Sign in with Google</a>
That’s the whole implementation. Drop those on a landing page or a SPA, and clicking the link signs the user in — PKCE, session storage, callback handling — all handled by the SDK without a byte of code from you.
What the SDK handles
- PKCE: generates the code verifier, stores it, sends the challenge
- State parameter: generates, stores, validates on callback
- Redirect URI: back to your origin, any path you want
- Token exchange: calls the token endpoint, gets back the ID token + access token
- Session storage: stores tokens in a way that balances security (no localStorage for refresh tokens) and usability (persists across reloads)
- JWKS validation: verifies the ID token signature against the provider’s public keys
All of that is boilerplate that otherwise consumes 200-500 lines of frontend code. The two-tag setup replaces it with two tags.
What you do
Once the user is signed in, you can:
// Read the current user
const user = await loginwith.getUser()
// Get an access token for API calls
const token = await loginwith.getAccessToken()
// Attach it to your API calls
fetch('/api/data', {
headers: { Authorization: `Bearer ${token}` }
})
// Sign out
await loginwith.signOut()
When this works
- Static sites (Astro, Next.js static export, plain HTML)
- SPAs without a backend
- Landing pages with a “try it” button
- Prototypes and MVPs
When you outgrow it
At some point you’ll want a backend. Common reasons:
- You need server-side sessions (e.g. for SSR)
- You need to call third-party APIs with secrets you don’t want in the browser
- You want to verify tokens server-side for audit logs
At that point, the same SDK flow works — the frontend still handles PKCE and gets the token; your backend validates the token on each API call. No rewrite required.
The philosophical point
Authentication should be the lowest-friction part of shipping a product, not the highest. “Drop in two tags” is the ceiling of how easy it can be. Any more complexity is architecture you’ve chosen, not complexity inherent to auth.