AppEngine supports several non-password sign-in methods, all collapsing into the same outcome: a redirect, a callback, an AppMint JWT. The provider's own access token is used once to verify identity, then thrown away — subsequent calls use the issued JWT.
The general pattern
- 1
Frontend redirects to AppEngine
The user clicks "Sign in with X". The browser navigates to
/profile/{provider}on AppEngine. - 2
AppEngine redirects to the provider
The passport strategy builds the provider's authorize URL with the right
client_id,scope, andredirect_uri, then 302s the browser there. - 3
User authenticates with the provider
On success, the provider 302s back to AppEngine's callback (
/profile/{provider}/redirect) with acodequery param. - 4
AppEngine exchanges the code, mints a JWT
The strategy calls the provider's token endpoint, fetches the user profile, finds-or-creates the local Customer/User record, and issues an AppMint JWT.
- 5
AppEngine redirects to your site with the token
The final 302 carries the JWT and refresh token in query params (or sets cookies, depending on configuration). Your site picks them up and stores them.
Configure each provider's credentials in your org settings (Studio Manager → Connect → Vendor connections). AppEngine reads them per-org, so the same instance can handle multiple tenants with different OAuth apps.
GitHub
/profile/githubNo auth/profile/github/urlNo auth/profile/github/redirectNo auth/profile/github/tokenNo auth/profile/github/redirectNo authGET /profile/github kicks off the redirect flow — point your "Sign in with GitHub" button at it. GET /profile/github/url returns the authorize URL as JSON if you'd rather initiate the redirect from the client. POST /profile/github/token is the AJAX-friendly variant: send { code, state } after handling the callback yourself, get a JWT back.
<a href="https://appengine.appmint.io/profile/github?orgid=my-org">
Sign in with GitHub
</a>
The callback URL you configure on GitHub's OAuth app should be:
https://appengine.appmint.io/profile/github/redirect
…unless you're handling the callback in your own app (with POST /profile/github/token), in which case point GitHub at your own callback route.
/profile/googleNo auth/profile/google/redirectNo authGoogle's flow is the simplest — passport-google-oauth20 with the standard email and profile scopes. No separate token-exchange endpoint; the redirect handler does it inline.
Configure the callback URL in Google Cloud Console as:
https://appengine.appmint.io/profile/google/redirect
/profile/facebookNo auth/profile/facebook/urlNo auth/profile/facebook/redirectNo auth/profile/facebook/tokenNo authSame shape as GitHub — both a server-redirect flow and an AJAX-style POST /profile/facebook/token for clients that want to handle the callback themselves.
Magic link (Customer)
Passwordless email sign-in for Customers. Send a link, user clicks it, they're signed in.
/profile/magic-linkNo auth/profile/magic-link/redirectNo auth# 1. Request the link
curl 'https://appengine.appmint.io/profile/[email protected]' \
-H 'orgid: my-org' \
-H 'x-client-host: myapp.com' \
-H 'x-client-protocol: https'
The x-client-host and x-client-protocol headers control where the link in the email points — useful when one AppEngine instance serves multiple frontends.
The user receives an email with a link of the form:
https://myapp.com/auth/[email protected]&token=...
Your frontend extracts those params and exchanges them for a JWT:
# 2. Exchange the token
curl -X POST https://appengine.appmint.io/profile/magic-link/redirect \
-H 'orgid: my-org' -H 'Content-Type: application/json' \
-d '{ "email": "[email protected]", "token": "..." }'
Response is the same { data: { token, refresh_token, user } } envelope.
Magic link (User)
Same flow for staff, different endpoints. Useful for IT-managed environments where passwords aren't desired.
/profile/user/magic-linkNo auth/profile/user/magic-link/redirectNo authOne-time code
Email-delivered numeric code rather than a clickable link — preferred for native mobile apps where deep linking is awkward.
/profile/code/{email}No authSends a short numeric code to the email. The user types it into your UI; you POST it back through a magic-link redirect (the code lands in the same token field).
Microsoft
A microsoft.strategy.ts ships in the source tree but is opt-in per-deployment. Endpoints follow the same /profile/microsoft / /profile/microsoft/redirect shape if enabled. Configure credentials and add the strategy to your UsersModule providers.
Validating a third-party profile
If your frontend handles the OAuth dance itself (some Next.js setups prefer this), POST the resolved profile to AppEngine and let it issue the JWT:
/profile/customer/social-loginNo authcurl -X POST https://appengine.appmint.io/profile/customer/social-login \
-H 'orgid: my-org' -H 'Content-Type: application/json' \
-d '{
"provider": "github",
"providerId": "12345",
"email": "[email protected]",
"firstName": "Alice",
"lastName": "Doe",
"avatar": "https://...",
"profile": { "...": "raw provider profile" }
}'
AppEngine matches by email first, falling back to provider + providerId. Returns { data: { token, refresh_token, user, isNewUser } }. The isNewUser: true flag is your cue to run the post-signup wizard.
This endpoint trusts the profile you send. If your frontend grabs the OAuth code, exchanges it for a token, and fetches the profile, your backend has already verified identity. Don't expose this endpoint to untrusted clients without server-side verification — anyone could claim any email.
State and CSRF
For redirect-based flows, AppEngine generates and validates the OAuth state parameter automatically. If you're using the POST /profile/{provider}/token variant, you're responsible for generating the state on the way out and validating it before posting back.
Failure handling
A failed callback redirects back to your configured failure URL with ?error=.... Common errors:
access_denied— user clicked Cancel.email_required— provider didn't return an email and your scope didn't include it. For GitHub, requestuser:email; the callback then makes a second call to/user/emailsto find the primary.provider_error— the token exchange returned an error. Check your client secret and callback URL.