Social sign-in setup
Brands and creators can log in or sign up with Google, Apple, Facebook, LinkedIn or X. The code is in place; each provider turns on the moment its credentials are in the environment. This page is for whoever runs the Partnely install.
How it works
- A button sends the browser to
/api/auth/{provider}/start, which stores a signed ten-minute state cookie and redirects to the provider. - The provider sends the browser back to
/api/auth/{provider}/callback. The code is exchanged for tokens server to server and reduced to one profile: id, email, whether the provider verified it, name, picture. - A known provider identity logs straight in. A Google or Apple sign-in whose confirmed email matches an account that has also confirmed that address links the identity to it; Facebook, LinkedIn and X never link by email. Anything else goes to
/signup/complete, where the person picks brand or creator and fills in the rest. No password is set; the account signs in with the provider from then on. - Only providers whose credentials are set show a button; with none set, the login and sign-up pages open on the email form.
Every provider needs the same redirect URI, built from APP_URL:
https://partnely.app/api/auth/<provider>/callback
If APP_URL changes, for example from a staging domain to the production one, update the URI in every provider console; providers reject mismatches outright.
Where: Google Cloud Console → APIs & Services → Credentials → Create credentials → OAuth client ID, type Web application. Open Google Cloud Console.
- Configure the OAuth consent screen first (External, app name, support email, the domain the app runs on). The scopes used are openid, email and profile, which need no verification.
- Create the OAuth client. Under Authorized redirect URIs add the callback URL below.
- Copy the Client ID and Client secret into the environment.
Redirect URI
https://partnely.app/api/auth/google/callback
Environment
GOOGLE_CLIENT_ID= GOOGLE_CLIENT_SECRET=
Notes
- Google returns a verified email, a display name and a picture, so sign-up needs only the brand or creator details.
- The consent screen can stay in Testing while you try it; add yourself as a test user.
Apple
Where: Apple Developer → Certificates, Identifiers & Profiles. Three objects are involved: an App ID, a Services ID, and a Sign in with Apple key. Open Apple Developer.
- Identifiers → App IDs: create one (or open the existing one) and enable the Sign in with Apple capability.
- Identifiers → Services IDs: create one, for example com.partnely.web. Enable Sign in with Apple, choose the App ID as its primary, and under Website URLs add the domain and the return URL below. This Services ID is APPLE_CLIENT_ID.
- Keys: create a key with Sign in with Apple enabled, tied to the same App ID. Download the .p8 once and note its Key ID (APPLE_KEY_ID).
- Your Team ID is under Membership details (APPLE_TEAM_ID). Put the .p8 contents in APPLE_PRIVATE_KEY; one line with \n between the PEM lines works.
Redirect URI
https://partnely.app/api/auth/apple/callback
Environment
APPLE_CLIENT_ID= APPLE_TEAM_ID= APPLE_KEY_ID= APPLE_PRIVATE_KEY=
Notes
- Apple sends the person's name only on the first authorization, as a form field next to the code. If it is missing later, the person removed the app under Settings → Apple ID → Sign in with Apple; they can authorize again to send it once more.
- The email may be a private relay address (@privaterelay.appleid.com). It works for login; mail to it is forwarded while the relay is active.
- Apple answers with a cross-site POST, so its state cookie is SameSite=None and Secure. The app must be served over HTTPS in production; localhost is treated as secure by browsers.
- The client secret is a short-lived JWT Partnely signs with the key on every token request; there is nothing to rotate manually.
Where: Meta for Developers → My Apps → Create app (Consumer or Business), then add the Facebook Login product. Open Meta for Developers.
- Facebook Login → Settings: add the callback URL below under Valid OAuth Redirect URIs.
- App settings → Basic: copy the App ID and App secret into the environment. Add the app domain and a privacy policy URL, which Meta requires before the app can go live.
- Switch the app from Development to Live mode once the basics are in; until then only app roles can sign in.
Redirect URI
https://partnely.app/api/auth/facebook/callback
Environment
FACEBOOK_APP_ID= FACEBOOK_APP_SECRET=
Notes
- The email and public_profile permissions are granted by default and need no App Review.
- Facebook gives no proof that an address was confirmed, so a Facebook sign-in is never linked to an existing account by email; the person confirms the address by email like a password sign-up. Some accounts have no email (phone sign-ups); those people type one on the completion page.
Where: LinkedIn Developer Portal → Create app. The app must be associated with a LinkedIn Page you administer. Open LinkedIn Developer Portal.
- Products: add “Sign In with LinkedIn using OpenID Connect”. It is approved instantly.
- Auth tab: under Authorized redirect URLs for your app add the callback URL below.
- Copy the Client ID and Primary Client Secret into the environment.
Redirect URI
https://partnely.app/api/auth/linkedin/callback
Environment
LINKEDIN_CLIENT_ID= LINKEDIN_CLIENT_SECRET=
Notes
- LinkedIn returns a verified email, the name and a picture through the OpenID userinfo endpoint. The older r_liteprofile flow is not used.
- LinkedIn identities are not linked to existing accounts by email either; the person confirms the address by email like a password sign-up.
X
Where: X Developer Portal → Projects & Apps → your app → User authentication settings. Open X Developer Portal.
- Enable OAuth 2.0. App permissions: Read. Type of app: Web App, Automated App or Bot (a confidential client).
- Callback URI / Redirect URL: the URL below. Website URL: the site's address.
- Keys and tokens → OAuth 2.0 Client ID and Client Secret: copy both into the environment. The secret is shown once.
Redirect URI
https://partnely.app/api/auth/x/callback
Environment
X_CLIENT_ID= X_CLIENT_SECRET=
Notes
- X does not share email addresses, so people signing up through X type theirs on the completion page and the identity is never linked to an existing account by email.
- Scopes requested are users.read, tweet.read and offline.access; X requires tweet.read alongside users.read to read the profile.
- PKCE is required by X and is always sent.
Environment
All variables together, as they appear in .env.example. Leave a provider's block empty to keep it off.
GOOGLE_CLIENT_ID= GOOGLE_CLIENT_SECRET= APPLE_CLIENT_ID= APPLE_TEAM_ID= APPLE_KEY_ID= APPLE_PRIVATE_KEY= FACEBOOK_APP_ID= FACEBOOK_APP_SECRET= LINKEDIN_CLIENT_ID= LINKEDIN_CLIENT_SECRET= X_CLIENT_ID= X_CLIENT_SECRET=
APPLE_PRIVATE_KEY is the contents of the .p8 file. Multi-line values are awkward in some hosts' settings screens, so the app also accepts the key on one line with a literal \n between the lines, including the BEGIN and END markers.
Checking it works
- Restart the server after changing the environment; credentials are read per request but some hosts cache the environment at start.
- Open
/loginand click the provider. A redirect straight back to the login page with a message means the credentials are missing or the callback was refused; the message says which. - A brand-new person lands on
/signup/complete. Someone signing in with Google or Apple whose confirmed email matches an account that has confirmed the same address is logged into it and the identity is linked, visible afterwards in theoauth_accountstable. - Server logs prefixed
[oauth]carry the provider's own error text when a token exchange fails.