Documentation

OAuth and passwordless sign-in

GitHub, Google, Facebook, magic link, and one-time-code sign-in flows.

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. 1

    Frontend redirects to AppEngine

    The user clicks "Sign in with X". The browser navigates to /profile/{provider} on AppEngine.

  2. 2

    AppEngine redirects to the provider

    The passport strategy builds the provider's authorize URL with the right client_id, scope, and redirect_uri, then 302s the browser there.

  3. 3

    User authenticates with the provider

    On success, the provider 302s back to AppEngine's callback (/profile/{provider}/redirect) with a code query param.

  4. 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. 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

GET/profile/githubNo auth
GET/profile/github/urlNo auth
GET/profile/github/redirectNo auth
POST/profile/github/tokenNo auth
POST/profile/github/redirectNo auth

GET /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.

Google

GET/profile/googleNo auth
GET/profile/google/redirectNo auth

Google'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

Facebook

GET/profile/facebookNo auth
GET/profile/facebook/urlNo auth
GET/profile/facebook/redirectNo auth
POST/profile/facebook/tokenNo auth

Same 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.

GET/profile/magic-linkNo auth
POST/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.

GET/profile/user/magic-linkNo auth
POST/profile/user/magic-link/redirectNo auth

One-time code

Email-delivered numeric code rather than a clickable link — preferred for native mobile apps where deep linking is awkward.

GET/profile/code/{email}No auth

Sends 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:

POST/profile/customer/social-loginNo auth
curl -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.

Validate the provider profile yourself

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, request user:email; the callback then makes a second call to /user/emails to find the primary.
  • provider_error — the token exchange returned an error. Check your client secret and callback URL.