Quickstart (5 minutes)
This assumes the managed IdP at https://1pass.dev. For self-hosting, see self-hosting.
Prerequisites
bash, curl, openssl, python3.
1. Create an account
https://start.1pass.dev/console/signup → email + password.
Email verification required before registering an app (RP)
Before you can register an app (RP) in the developer console, you must verify your email. After signing up, click the link in the verification email, or resend it from the console at /account. Registration is blocked until you verify. → App registration guide · Prerequisite
2. Register an OAuth app
At https://start.1pass.dev/developer/applications/new:
- Redirect URI:
http://localhost:4000/auth/callback - Client type: Confidential (you have a backend) / Public (mobile, SPA, or standalone CLI)
Right after you save, the client_secret is shown only once — store it in .env immediately. If you lose it, you can only re-issue a new one.
App detail → Manage Scopes → enable openid, profile:basic, and email.
If you need an id_token, openid is required
Reading identity straight from the id_token payload (common in SPAs and serverless) requires the openid scope. Without it, the token response omits the id_token, and identity can only be retrieved via userinfo. profile is an alias for profile:basic, but standardize on profile:basic for both registration and requests. — Scope reference
Deciding the client type: Public vs Confidential.
3. Environment variables
export LOGI="https://api.1pass.dev"
export CLIENT_ID="logi_..."
export CLIENT_SECRET="..." # omit for a Public client
export REDIRECT="http://localhost:4000/auth/callback"Endpoint discovery: $LOGI/.well-known/openid-configuration.
4. Generate a PKCE pair
VERIFIER=$(openssl rand -hex 32)
CHALLENGE=$(printf '%s' "$VERIFIER" \
| openssl dgst -sha256 -binary \
| python3 -c 'import sys,base64; print(base64.urlsafe_b64encode(sys.stdin.buffer.read()).rstrip(b"=").decode())')
echo "verifier = $VERIFIER"
echo "challenge = $CHALLENGE"5. Get an Authorization Code
open "$LOGI/oauth/authorize?\
client_id=$CLIENT_ID&\
redirect_uri=$REDIRECT&\
response_type=code&\
scope=openid+profile:basic+email&\
state=random_xyz&\
code_challenge=$CHALLENGE&\
code_challenge_method=S256"→ http://localhost:4000/auth/callback?code=...&state=random_xyz. Copy the code value.
Show only specific login methods on the login screen
If you want to surface only specific login methods — for example, you use Google Workspace exclusively across the company — add a provider parameter to the authorize URL (e.g. &provider=google shows only the "Continue with Google" button). You can also enforce this per app in the console. For the full behavior, see login method restriction.
6. Exchange for tokens
CODE="the_code_you_copied"
curl -s -X POST "$LOGI/oauth/token" \
-d grant_type=authorization_code \
-d code=$CODE \
-d redirect_uri=$REDIRECT \
-d code_verifier=$VERIFIER \
-d client_id=$CLIENT_ID \
-d client_secret=$CLIENT_SECRETPublic client: remove the -d client_secret=... line. Sending it triggers an invalid_client rejection.
curl -s -X POST "$LOGI/oauth/token" \
-d grant_type=authorization_code \
-d code=$CODE \
-d redirect_uri=$REDIRECT \
-d code_verifier=$VERIFIER \
-d client_id=$CLIENT_IDResponse:
{
"access_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6Ii4uLiJ9...",
"token_type": "Bearer",
"expires_in": 900,
"refresh_token": "DWxB...",
"id_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6Ii4uLiJ9...",
"scope": "openid profile:basic email"
}The id_token is included only when you requested the openid scope.
7. Look up identity — two ways
7a. Read sub from the id_token (no re-verification of the signature)
If you received the /oauth/token response directly over TLS, you can trust the sub in the id_token payload right away. Note that the id_token carries only the standard OIDC claims like sub — it does not include email or name.
echo "$ID_TOKEN" | cut -d. -f2 | base64 -d 2>/dev/null | python3 -m json.tool
# → {"iss":"logi","sub":"1","aud":"logi_...","exp":...,"iat":...,"at_hash":"..."}If you received it straight from the token endpoint, re-verifying the signature is optional
An id_token received directly over TLS from /oauth/token within the same backend request can be trusted at the payload level (OIDC §3.1.3.7 #6 exception). Verifying the signature against JWKS is required only when the id_token arrives over an untrusted path (front-channel, stored and reused, or relayed). → JWKS verification
7b. Get the profile (email, name) from userinfo
Profile fields like email and name are not in the id_token, so call userinfo with the access_token.
ACCESS_TOKEN="access_token_from_the_response_above"
curl -s -H "Authorization: Bearer $ACCESS_TOKEN" "$LOGI/oauth/userinfo"
# → {"sub":"1","nickname":"Gildong","name":"Hong Gildong","email":"user@example.com","email_verified":true}Troubleshooting
invalid_client: a Public client sent aclient_secret— remove the line.- PKCE: S256 is required,
plainis rejected.redirect_uriallows only https / 127.0.0.1 / localhost / custom-scheme.
Next steps
- Web: Next.js · SPA + serverless (Vercel) · Rails 8 · Express
- Mobile: iOS · Android · Flutter · React Native
- Button components · Refresh rotation · Webhooks · Account Merge · Security