Auth0 Setup Tutorial
Auth0 Setup Tutorial: Tenant, Angular App, API, Google Login, Optional Passkeys, and Email OTP
Section titled “Auth0 Setup Tutorial: Tenant, Angular App, API, Google Login, Optional Passkeys, and Email OTP”This guide is for a small community tool that wants production-style authentication without building password auth from scratch.
The target setup:
Angular UI -> Auth0 Universal Login -> Google login through Auth0 -> Auth0 issues tokens -> ASP.NET Core API validates Auth0 access token -> API maps Auth0 user to local app user by Auth0 subOptional later:
PasskeysEmail OTP passwordlessAccount linking, if multiple login methods should become one Obaki accountImportant decision:
Use Auth0 sub as identity.Use email only as profile/contact data.Do not merge users by email automatically.Source references
Section titled “Source references”Primary references used while preparing this guide:
- Auth0 ASP.NET Core Web API quickstart: https://auth0.com/docs/quickstart/backend/aspnet-core-webapi
- Auth0 Angular SPA quickstart: https://auth0.com/docs/quickstart/spa/angular
- Auth0 Angular SDK docs: https://auth0.com/docs/libraries/auth0-angular-spa
- Auth0 application settings: https://auth0.com/docs/get-started/applications/application-settings
- Auth0 API settings: https://auth0.com/docs/get-started/apis/api-settings
- Auth0 Google social connection: https://auth0.com/docs/authenticate/identity-providers/social-identity-providers/google
- Auth0 social identity providers: https://auth0.com/docs/authenticate/identity-providers/social-identity-providers
- Auth0 passkeys: https://auth0.com/docs/authenticate/database-connections/passkeys
- Auth0 passwordless authentication: https://auth0.com/docs/authenticate/passwordless
- Auth0 account linking: https://auth0.com/docs/manage-users/user-accounts/user-account-linking
- Auth0 RBAC: https://auth0.com/docs/manage-users/access-control/configure-core-rbac
- Auth0 custom claims: https://auth0.com/docs/secure/tokens/json-web-tokens/create-custom-claims
- Google OAuth consent screen docs: https://developers.google.com/workspace/guides/configure-oauth-consent
1. Terms you need to understand first
Section titled “1. Terms you need to understand first”Auth0 tenant
Section titled “Auth0 tenant”Your Auth0 account container. It owns your domain, applications, APIs, users, connections, roles, actions, logs, and security settings.
Example tenant domain:
jjosh-dev.us.auth0.comAuth0 application
Section titled “Auth0 application”The client application that sends the user to Auth0 for login.
For Angular, choose:
Single Page ApplicationAuth0 API
Section titled “Auth0 API”A registered API resource in Auth0. This represents your ASP.NET Core API. It gives you an audience value that your Angular app requests and your .NET API validates.
Example API identifier:
https://api.jjosh.devThis does not need to be a real reachable URL, but it should be stable, unique, and controlled by you.
Connection
Section titled “Connection”An identity source Auth0 can use for login.
Examples:
Google OAuth2Username-Password-Authenticationemail passwordlessFacebookGitHubMicrosoftFor Obaki, start with Google.
Passkeys are configured on Auth0 database connections. Email OTP is configured as an Auth0 passwordless connection. Google is a social connection.
Universal Login
Section titled “Universal Login”Auth0-hosted login page. Your app redirects users there instead of building its own login form.
Use Universal Login unless you have a strong reason not to.
ID token vs access token
Section titled “ID token vs access token”Use them correctly.
ID token- For the frontend app- Tells the UI who signed in- Contains identity/profile claims- Do not use it to call your API
Access token- For the API- Sent as Bearer token- Validated by ASP.NET Core- Contains audience, issuer, subject, scopes, and optionally permissions/custom claimsYour Angular app should call your API with the access token, not the ID token.
Auth0 sub
Section titled “Auth0 sub”The stable subject claim from the token.
Example values:
google-oauth2|123auth0|456email|user@example.comObaki should store the full sub value as the external identity key.
Part A: Create the Auth0 tenant
Section titled “Part A: Create the Auth0 tenant”Step 1. Create Auth0 account
Section titled “Step 1. Create Auth0 account”Go to:
https://auth0.com/signupCreate an account.
Use your real developer email.
Step 2. Create tenant
Section titled “Step 2. Create tenant”When prompted, create a tenant.
Recommended naming:
jjosh-devUse a region close to your expected users if Auth0 gives you a region choice.
For a Philippine community tool, any nearby supported region is acceptable. Do not over-optimize this early.
Step 3. Record your Auth0 domain
Section titled “Step 3. Record your Auth0 domain”In Auth0 Dashboard:
Applications > Applications > Your Application > SettingsYou will see a domain like:
YOUR_TENANT.REGION.auth0.comStore it as:
AUTH0_DOMAIN=YOUR_TENANT.REGION.auth0.comDo not include https:// in the plain domain environment value unless your code expects it.
Part B: Create the Angular application in Auth0
Section titled “Part B: Create the Angular application in Auth0”Step 1. Create application
Section titled “Step 1. Create application”In Auth0 Dashboard:
Applications > Applications > Create ApplicationUse:
Name: Obaki WebApplication Type: Single Page ApplicationChoose Angular if Auth0 asks for technology.
Step 2. Save application values
Section titled “Step 2. Save application values”From the application settings page, copy:
DomainClient IDYou need these in Angular.
Do not expose a client secret in Angular. A SPA is a public client. It cannot safely keep secrets.
Step 3. Configure local URLs
Section titled “Step 3. Configure local URLs”For local Angular development, use:
Allowed Callback URLs:http://localhost:4200
Allowed Logout URLs:http://localhost:4200
Allowed Web Origins:http://localhost:4200If your Angular callback route is /callback, use this instead:
Allowed Callback URLs:http://localhost:4200/callbackKeep the configured Auth0 callback URL and Angular SDK redirect URI exactly aligned.
Step 4. Configure production URLs
Section titled “Step 4. Configure production URLs”When deployed, add your real frontend URL.
Example:
Allowed Callback URLs:http://localhost:4200, https://jjosh.dev
Allowed Logout URLs:http://localhost:4200, https://jjosh.dev
Allowed Web Origins:http://localhost:4200, https://jjosh.devIf the app is deployed to a subdomain:
https://app.jjosh.devuse that exact value.
Avoid wildcard callback URLs in production.
Bad:
https://*.jjosh.devBetter:
https://app.jjosh.devAuth0 warns that callback URLs and allowed origins should use exact URLs, and production apps should not rely on localhost or broad wildcard patterns.
Step 5. Enable only the intended connections
Section titled “Step 5. Enable only the intended connections”Inside the application settings, look for a tab or section named:
ConnectionsRecommended early setup:
Enable:- Google
Disable at first:- Username-Password-Authentication- email passwordless- FacebookAdd passkeys or email OTP later only when you intentionally want extra login methods.
Part C: Create the API in Auth0
Section titled “Part C: Create the API in Auth0”Step 1. Create API
Section titled “Step 1. Create API”In Auth0 Dashboard:
Applications > APIs > Create APIUse:
Name: Obaki APIIdentifier: https://api.jjosh.devSigning Algorithm: RS256Use RS256. Auth0’s ASP.NET Core API quickstart uses the API identifier as the audience, and Auth0’s default API signing algorithm is RS256.
Step 2. Store API values
Section titled “Step 2. Store API values”Record:
AUTH0_DOMAIN=YOUR_TENANT.REGION.auth0.comAUTH0_AUDIENCE=https://api.jjosh.devThe Angular app will request this audience.
The ASP.NET Core API will validate this audience.
Step 3. Add API permissions
Section titled “Step 3. Add API permissions”In the API settings, add permissions/scopes.
Start minimal:
read:memanage:own_notificationsadmin:manage_notificationsUse scopes for API capability checks. Use your own database for domain-specific ownership rules.
Example:
Scope says: this token can manage notification rules.Database says: this specific rule belongs to this specific user.Do both.
Part D: Configure Universal Login
Section titled “Part D: Configure Universal Login”Step 1. Open branding settings
Section titled “Step 1. Open branding settings”In Auth0 Dashboard:
Branding > Universal LoginSet:
Application nameLogoPrimary colorBackground colorKeep branding simple.
Step 2. Use Universal Login
Section titled “Step 2. Use Universal Login”Avoid custom login-page work until login is working end-to-end.
Recommended first phase:
Default Universal LoginLogoApp nameSimple brand colorGoogle connection onlyStep 3. Test redirects before adding login methods
Section titled “Step 3. Test redirects before adding login methods”Before tuning providers, test that the Auth0 application redirects correctly.
Use Auth0’s built-in test button or Angular quickstart flow.
Part E: Add Google login
Section titled “Part E: Add Google login”Google should be the first login method for this project.
Why:
- Users already understand Google login- No Obaki-owned passwords- Lower setup friction than account linking- Auth0 still issues standard OIDC tokens to Angular and the APIStep 1. Create a Google Cloud project
Section titled “Step 1. Create a Google Cloud project”Go to:
https://console.cloud.google.comCreate or select a project.
Suggested name:
Obaki AuthStep 2. Configure OAuth consent screen
Section titled “Step 2. Configure OAuth consent screen”In Google Cloud Console:
APIs & Services > OAuth consent screenConfigure:
App name: ObakiUser support email: your emailDeveloper contact email: your emailAudience: ExternalAuthorized domain: jjosh.dev, if deploying under jjosh.devFor basic login, request only basic OIDC scopes:
openidprofileemailAvoid sensitive or restricted scopes.
You do not need Drive, Gmail, Calendar, or other Google API scopes for simple sign-in.
Step 3. Create OAuth client credentials in Google
Section titled “Step 3. Create OAuth client credentials in Google”In Google Cloud Console:
APIs & Services > Credentials > Create Credentials > OAuth client IDUse:
Application type: Web applicationName: Auth0 Google LoginAdd authorized redirect URI:
https://YOUR_AUTH0_DOMAIN/login/callbackExample:
https://jjosh-dev.us.auth0.com/login/callbackThis is not your Angular callback URL.
Google redirects back to Auth0. Auth0 then redirects back to your Angular app.
Copy:
Google Client IDGoogle Client SecretStep 4. Add Google connection in Auth0
Section titled “Step 4. Add Google connection in Auth0”In Auth0 Dashboard:
Authentication > Social > Create Connection > Google / OAuth2Paste:
Client IDClient SecretEnable basic profile and email.
Use minimal scopes:
openid profile emailStep 5. Enable Google connection for your Angular app
Section titled “Step 5. Enable Google connection for your Angular app”In the Google connection settings, enable it for:
Obaki WebOr go to:
Applications > Applications > Obaki Web > ConnectionsEnable Google.
Step 6. Test Google login
Section titled “Step 6. Test Google login”Use:
Authentication > Social > Google > Try ConnectionThen test from Angular.
Expected result:
Angular redirects to Auth0Auth0 shows Google optionUser signs in with GoogleAuth0 redirects back to AngularAngular shows authenticated stateAngular can request API access tokenAPI accepts the Bearer token/api/me creates or refreshes the local Obaki userExpected Auth0 subject shape:
google-oauth2|...The exact value does not matter. Obaki stores and matches the full sub.
Google troubleshooting
Section titled “Google troubleshooting”Redirect URI mismatch
Section titled “Redirect URI mismatch”Cause:
Google authorized redirect URI does not match Auth0 callback URL.Fix:
Google Cloud Console > Credentials > OAuth Client > Authorized redirect URIsMake sure this exact value exists:
https://YOUR_AUTH0_DOMAIN/login/callbackAuth0 logs in but API returns 401
Section titled “Auth0 logs in but API returns 401”Usually the Angular app did not request the API audience.
Angular must request:
audience: https://api.jjosh.devThe API must validate:
Authority: https://YOUR_AUTH0_DOMAIN/Audience: https://api.jjosh.devUser signed in, but email is missing
Section titled “User signed in, but email is missing”Do not assume every provider always gives every claim.
For Google, request:
openid profile emailFor the backend, do not depend on email as identity. Use sub.
Part F: Optional passkeys
Section titled “Part F: Optional passkeys”Add passkeys later only if you intentionally want a non-Google login path.
Passkeys can be useful when:
- A user does not want to use Google- You want a phishing-resistant fallback- You want passwordless login without relying on a social providerPasskeys have an identity tradeoff:
Google user: google-oauth2|123Passkey/database user: auth0|456Those can be different Auth0 users even when the visible email address is the same.
Step 1. Choose the database connection
Section titled “Step 1. Choose the database connection”In Auth0 Dashboard, open the database connection you want to use for Obaki.
Common default:
Username-Password-AuthenticationThis name is confusing. For this setup, think of it as the Auth0 database connection that owns passkey users.
Step 2. Configure passkey policy
Section titled “Step 2. Configure passkey policy”In the database connection settings, enable or configure passkeys.
Use the Auth0 passkey policy defaults at first unless you have a clear reason to change them.
If Auth0 asks where passkeys are available, enable them for:
Obaki WebDo not add custom passkey code to Angular. Let Auth0 Universal Login own the browser and device ceremony.
Step 3. Test passkey signup and login
Section titled “Step 3. Test passkey signup and login”Expected signup flow:
Angular redirects to Auth0Auth0 asks for emailUser creates passkeyAuth0 redirects back to AngularAngular can request API access tokenAPI accepts the Bearer token/api/me creates or refreshes a local Obaki user by Auth0 subIf the same person previously used Google, do not expect passkey login to automatically land on the same Obaki user until account linking exists.
Part G: Optional email OTP passwordless
Section titled “Part G: Optional email OTP passwordless”Only add email OTP if Google alone is too restrictive and passkeys are not enough.
Email OTP is useful when:
- A user cannot or will not use Google- A user cannot create a passkey- You want a simple fallback without storing passwordsEmail OTP has the same identity tradeoff:
Google user: google-oauth2|123Email passwordless user: email|same-address@example.comPasskey/database user: auth0|456Those can be different Auth0 users even when the visible email address is the same.
Step 1. Create or configure email passwordless
Section titled “Step 1. Create or configure email passwordless”In Auth0 Dashboard:
Authentication > PasswordlessChoose email one-time password.
Do not choose magic links for this Universal Login setup. Auth0 documents magic links as Classic Login only.
Step 2. Configure the email sender
Section titled “Step 2. Configure the email sender”Use Auth0’s built-in sender for early testing if acceptable for your tenant.
Before production, configure the sender/from address according to Auth0’s email-provider requirements and your domain ownership.
Do not use Obaki’s contact-email system for Auth0 login codes. Auth0 should send login codes itself.
Step 3. Enable email passwordless for Obaki Web
Section titled “Step 3. Enable email passwordless for Obaki Web”Enable the email passwordless connection for:
Obaki WebKeep other optional connections disabled unless intentionally supported.
Step 4. Test email OTP
Section titled “Step 4. Test email OTP”Expected flow:
Angular redirects to Auth0User enters emailAuth0 sends one-time codeUser enters codeAuth0 redirects back to AngularAngular can request API access tokenAPI accepts the Bearer token/api/me creates or refreshes a local Obaki user by Auth0 subRecord the resulting sub during testing.
If the same email already has a Google user, do not expect email OTP to automatically land on the same local Obaki user.
Part H: Same-email and account-linking issue
Section titled “Part H: Same-email and account-linking issue”Yes, this is the important issue.
You can capture an email claim when Auth0 gives one, but you cannot use email as the stable account key.
Reasons:
- A user may type a different email later- Providers can omit email- Email can change- Email verification state can vary- Auth0 treats different connections as separate identities by default- Passwordless email users are separate from database/social users unless linkedCorrect Obaki rule:
Local Users table key: Auth0 subLocal Users email: optional profile dataDo not do this:
Find local user by email and silently attach a new Auth0 subThat can let the wrong person into an existing account if email ownership or provider behavior is misunderstood.
Do this instead:
Treat new Auth0 sub as a new local user.Add account linking later only after both identities are authenticated.Auth0 account linking can associate identities after the user proves control of both accounts. That is the right later feature if you support multiple login methods.
Part I: Obaki backend fit
Section titled “Part I: Obaki backend fit”The current backend shape already fits the Google-first scheme.
Current good behavior:
/api/me reads the token sub claimGetCurrentUser stores it as AppUser.Auth0UserIdusers.auth0_user_id has a unique indexemail, display name, and avatar are profile fields onlyJWT validation checks issuer, audience, signature, and lifetimeThat means Google login does not require a backend refactor.
Current limitation for future account linking:
Notification subscriptions are owned by a hash of the current Auth0 sub.That is fine for Google-only login.
If you later let the same person sign in through Google, passkey, and email OTP before linking them, Obaki will treat each distinct Auth0 sub as a distinct account. Their subscriptions will not automatically appear across login methods.
If account linking becomes a real product feature, plan one small backend follow-up:
Centralize current-user resolution for protected endpoints.Prefer local AppUser.Id as the ownership key for user-owned data.Add a merge/migration path for subscriptions created under secondary Auth0 subjects.Do not refactor to email-based identity.
Part J: Recommended Auth0 settings for this project
Section titled “Part J: Recommended Auth0 settings for this project”Application type
Section titled “Application type”Single Page ApplicationGrant flow
Section titled “Grant flow”For Angular SPA, use Auth0 Angular SDK. It uses Authorization Code Flow with PKCE under the hood.
Do not implement OAuth manually.
Token signing
Section titled “Token signing”Use:
RS256Login methods
Section titled “Login methods”Start:
GoogleOptional later:
PasskeysEmail OTP passwordlessAvoid early:
FacebookSMS passwordlessUsername/password as a promoted login methodAutomatic account linkingDo not require MFA for normal community users early.
Use MFA later for admin accounts if your Auth0 plan and configuration support it.
Roles and permissions
Section titled “Roles and permissions”Use Auth0 RBAC for broad API permissions only.
Use your database for ownership and domain rules.
Example:
Auth0 permission: manage:own_notificationsDatabase rule: notification rule belongs to current local userPart K: Production rollout checklist
Section titled “Part K: Production rollout checklist”Before releasing publicly:
[ ] Auth0 Angular application created as SPA[ ] Auth0 API created with stable audience[ ] Allowed Callback URLs include production frontend URL[ ] Allowed Logout URLs include production frontend URL[ ] Allowed Web Origins include production frontend URL[ ] No broad wildcard callback URLs[ ] Google social connection uses your own Google client ID/secret[ ] Google OAuth consent screen configured[ ] API validates issuer and audience[ ] API requires HTTPS in production[ ] API CORS allows only your frontend origin[ ] Local app user is keyed by Auth0 `sub`, not email[ ] Email OTP passwordless is disabled unless intentionally supported[ ] Passkeys are disabled unless intentionally supported[ ] If optional login methods are enabled, duplicate-account behavior is documented[ ] Account linking is not automatic or email-only[ ] Admin-only endpoints require an admin policy[ ] Abuse limits exist for community-created data[ ] Logs do not print raw tokens[ ] Secrets are stored outside source controlPart L: Local development values
Section titled “Part L: Local development values”Example Auth0 values:
AUTH0_DOMAIN=jjosh-dev.us.auth0.comAUTH0_AUDIENCE=https://api.jjosh.devAUTH0_CLIENT_ID=abc123YOURCLIENTIDExample Angular environment:
export const environment = { production: false, apiBaseUrl: 'https://localhost:5001', auth0: { domain: 'jjosh-dev.us.auth0.com', clientId: 'abc123YOURCLIENTID', authorizationParams: { audience: 'https://api.jjosh.dev', redirect_uri: window.location.origin } }};Example API appsettings:
{ "Auth0": { "Enabled": true, "Domain": "jjosh-dev.us.auth0.com", "Audience": "https://api.jjosh.dev", "ClaimNamespace": "https://jjosh.dev/claims" }, "Cors": { "AllowedOrigins": [ "http://localhost:4200" ] }}Part M: Recommended testing sequence
Section titled “Part M: Recommended testing sequence”Do this in order.
1. Auth0 tenant exists2. Angular SPA application exists3. Angular app can redirect to Auth0 and back4. Auth0 API exists5. Angular requests access token with API audience6. ASP.NET Core API validates token7. Protected /api/me endpoint works8. Google connection works9. Google login returns a stable google-oauth2 Auth0 sub10. Local user row is created on first authenticated API call11. Account-owned notification endpoints require authentication12. Existing subscriptions remain attached across Google logouts/logins13. Optional passkey login creates a known, tested Auth0 sub shape14. Optional email OTP creates a known, tested Auth0 sub shape15. Optional duplicate-user behavior is accepted or account linking is implemented16. Admin endpoints reject normal usersDo not add passkeys or email OTP before Google login works.
Do not add RBAC before basic JWT validation works.
Do not add account linking before one-provider login works.
Part N: Minimal decision for your project
Section titled “Part N: Minimal decision for your project”Use this first:
Auth0 tenantAngular SPA appAuth0 API audienceGoogle social connectionASP.NET Core JWT validationLocal Users table keyed by Auth0 subAdd this second only if needed:
Passkey loginEmail OTP passwordless fallbackAdmin roleRBAC permissionsCustom claims, if neededAccount linking, if multiple login methods should share one Obaki accountAvoid this early:
Custom login UIApp-owned password authFacebook loginSMS loginAccount linking automationOrganizationsSAML/enterprise connectionsFine-grained authorization product