Appearance
Are you an LLM? You can read better optimized documentation at /docs/app/account-sign-in-and-permissions.md for this page in Markdown format
Account, sign-in, and permissions
- Does ShootCal ever see or store my Google password?
- Can the gmail.send scope read my email?
- Are ShootCal's Google permissions verified?
- If I grant an extra scope later, do I lose my existing Calendar permission?
- Does the drive.file scope let ShootCal read my whole Google Drive?
- How is my Google connection protected on the server?
- How do I completely revoke ShootCal's access, and what happens in the app afterward?
- What stops one ShootCal web user from seeing another user's calendar?
- What's the difference between a full sign-out and the "expired credentials" path on native?
One ShootCal account works across the web app, Galleries, and the Lightroom Classic plugin. On the web, sign in with your ShootCal email and password or continue with Google. If you start with Google, ShootCal asks you to choose a ShootCal password once before entering the web app or Galleries. The native Apple and Android apps use Google sign-in. ShootCal never sees your Google password.
When you sign in, ShootCal asks Google for permission to do specific things on your behalf, like read and create calendar events, save clients to Google Contacts, and send confirmation emails from your own Gmail. You see exactly what you are agreeing to on Google's consent screen, and you can decline.
A few extra features ask for permission only the first time you use them, not at sign-in. Tasks and the "find client emails from my Google contacts" scan are two examples. This keeps the normal sign-in simple and avoids an extra warning screen for features you may never turn on.
While those few features are still being reviewed by Google, turning one on may show a "this app is not verified by Google" notice. That is a normal step for a newer app and does not mean anything is wrong. You can always remove ShootCal's access to your Google account at any time from your Google Account security page, with no help from us needed.
One ShootCal account, with a secure Google connection
Your verified email is your stable ShootCal identity. The web app, Galleries, and Lightroom use one ShootCal password, stored only as a one-way hash. Google sign-in verifies your identity and connects the Google features ShootCal uses; it does not create a second ShootCal account.
When you choose Continue with Google on the web, the browser is sent to Google's consent screen, then Google sends you back to ShootCal once you approve. ShootCal uses Google's standard secure login flow (OAuth with PKCE), so ShootCal never sees your Google password and the login handshake is protected end to end. Google's identity proof comes straight to ShootCal's server, and the server checks that it was issued by Google for ShootCal before trusting it. (The full handshake details, including how the short-lived handshake cookie is protected, are on the privacy page.)
On the native Apple apps (iPhone, iPad, and Mac) and the native Android app, sign-in remains Google-only through the platform's official Google authentication flow. It presents Google's screen and can restore a prior session later so you do not have to sign in every time.
The exact scopes requested at sign-in, and why each is needed
When Google is connected, the web and native apps request overlapping but not identical permission sets. They share the calendar, contacts, and gmail.send scopes, while the web Google connection also asks for basic identity (openid, email, profile) and a Drive scope for the contract archive. The native flow supplies identity through Google's platform authentication, and Drive archiving is web-only. ShootCal requests:
- openid, email, profile (web), basic identity: your Google account id, email, and name/photo. This is what tells ShootCal who you are. (On native, identity comes from the GoogleSignIn SDK, so these are not in the native scope list.)
- calendar.events, create and edit your sessions/bookings as calendar events.
- calendar.readonly, list your calendars and read existing events.
- contacts, the client manager, which reads and writes Google Contacts so clients sync.
- gmail.send, send the photographer's own notifications and client messages — including booking confirmations, reminders, replies, and invoice notices — from their connected Gmail. This is send-only: it cannot read the mailbox. If you decline it, sends fail gracefully and the underlying record is still saved.
- drive.file (web only), used by the signed-contract archive. This scope only ever lets the app see or manage files it created itself, so it can only ever reach ShootCal's own "ShootCal Contracts" folder. It cannot browse the rest of your Drive.
All of these are Google-classified as "sensitive," not "restricted" (there is no full-mailbox read), so they need consent-screen verification but not a paid third-party security audit.
Incremental consent: Tasks and read-only "Other contacts" are asked on first use, not at sign-in
Two scopes are deliberately kept OUT of the sign-in request and asked for only when you first use the feature that needs them:
- Other contacts (read-only), read-only access to your auto-saved "Other contacts" (people you have emailed), used by the optional "find client emails from my Google contacts" step of the calendar scan.
Why split it out? It's a privacy-first design: this is a narrow, optional feature, so users who never turn it on never grant the permission. (All of ShootCal's Google permissions completed Google's OAuth verification in July 2026 — no warnings appear anywhere.)
On the web, connecting one of these features re-runs Google consent and adds only the new scope. You must already be signed in. The request explicitly forces Google to show the consent screen even to a returning user, and tells Google to keep all of your existing grants so the resulting token carries both the old and new permissions.
On native, the app asks the SDK to add just the new scope, which presents consent WITHOUT a full sign-out, so your existing Calendar grant is never lost. Tasks additionally has a hard guard: that opt-in flow is the only place the Tasks scope is ever requested, so nothing else in the app can trigger the consent prompt.
Note: gmail.send and contacts were also incremental historically, but were later moved into the sign-in bundle; the native apps now bundle Tasks at sign-in too. Only the "Other contacts" scope remains strictly incremental, tied to its opt-in feature.
Verification status
All of ShootCal's Google permissions are fully verified by Google (OAuth app verification completed July 2026), so no "unverified app" notice appears at sign-in or when enabling any feature. When the web app loads, the server tells it which optional permissions you currently hold, so the UI shows a "Connect Google Tasks" prompt where needed rather than silently failing.
Where the data actually lives
Web: After a successful sign-in, your Google connection is stored on ShootCal's server, encrypted before it is written to disk and kept outside the public website. Your browser holds ONLY a meaningless session token in a locked-down cookie; the server stores only a scrambled, one-way version of it, so a database leak yields no usable cookies. Your identity is figured out solely from that server-side login record, never from anything the browser sends, which is the guarantee that one user can never see another user's calendar. You stay signed in for up to 90 days, refreshed each time you use the app. (The encryption is covered in full on the privacy page.)
Native: OAuth tokens are held by Google's GoogleSignIn SDK, which stores them in the device Keychain; the app's own small Keychain wrapper stores separate web-integration tokens (session/push tokens) as generic-password items, explicitly NOT iCloud-synced. The native app keeps a live snapshot of your granted scopes so the UI can show exactly which permissions you have granted.
The access token is short-lived; the app refreshes it from the stored refresh token as needed, on both the server and natively.
Revoking access and signing out
There are two distinct actions:
Signing out ends your ShootCal session but does not revoke Google's grant. On web, signing out deletes the server session row and clears the cookie. On native, it clears the local session and purges cached event files and the saved calendar selection for a clean reset. The web app can also hold several Google accounts at once: the account menu in the header lets you add and switch between them without signing in again. If you are signed in to more than one, signing out ends only the active account's session and switches you to the next; signing out of the last account ends the session entirely.
Revoking ShootCal's access to your Google account is done on Google's side, not inside ShootCal: visit your Google Account, then Security, then "Third-party apps with account access" (myaccount.google.com/permissions) and remove ShootCal. ShootCal is built to handle this gracefully: when a Google call later fails because the grant was revoked, the native app clears the dead session and re-shows the sign-in prompt (a light reset that keeps your cached events so re-consenting is seamless). A non-network token-refresh failure is treated the same way.
FAQ
Does ShootCal ever see or store my Google password?
No. Google handles Google sign-in on its own pages and returns only access tokens and a signed proof of who you are. ShootCal stores those Google tokens encrypted on the web backend or in secure device storage on native, never your Google password. Your separate ShootCal password for the web app, Galleries, and Lightroom is stored only as a one-way hash; the original password cannot be recovered from it.
Can the gmail.send scope read my email?
No. The gmail.send scope is send-only and cannot read the mailbox. It is used solely to send the photographer's own notifications and client messages, including booking confirmations, reminders, replies, and invoice notices, from their connected Gmail. None of ShootCal's scopes include full-mailbox read; all requested scopes are Google-classified 'sensitive,' not 'restricted.'
Are ShootCal's Google permissions verified?
Yes. ShootCal completed Google's OAuth app verification for every permission it requests (July 2026), so you will never see an "unverified app" warning — not at sign-in and not when enabling any feature. If you connected Tasks before verification completed, your grant carries over unchanged.
If I grant an extra scope later, do I lose my existing Calendar permission?
No. On web, the incremental consent request tells Google to keep all prior grants, so the new token carries both old and new scopes. On native, the SDK adds the new scope without a full sign-out, so your existing Calendar grant is untouched. The native app keeps a live snapshot of your granted scopes so the UI reflects exactly what you hold.
Does the drive.file scope let ShootCal read my whole Google Drive?
No. The Drive scope (web only, used for the signed-contract archive) limits the app to files it created itself. It can only ever reach ShootCal's own 'ShootCal Contracts' folder. It cannot browse or read the rest of your Drive.
How is my Google connection protected on the server?
It is encrypted before it is written to disk, with the key kept in a separate file outside the public website, so a database leak alone yields nothing usable. Your browser never holds the Google token; it holds only a meaningless session cookie, and the server stores only a scrambled, one-way version of it, not the value itself. The privacy page covers the exact encryption.
How do I completely revoke ShootCal's access, and what happens in the app afterward?
Signing out inside ShootCal only ends your session. To revoke the OAuth grant, go to your Google Account security page (myaccount.google.com/permissions) and remove ShootCal. Afterward, the next Google call fails, the native app clears the session and re-shows the sign-in prompt while keeping cached events so re-consenting is seamless.
What stops one ShootCal web user from seeing another user's calendar?
Server-side isolation. Your identity is figured out solely from the server's own login record, matched to your session cookie; the browser never sends a user id or calendar owner that the server trusts. Every request resolves the user from that record, so a user can only ever reach their own connected Google data.
What's the difference between a full sign-out and the "expired credentials" path on native?
Signing out is a full reset: it clears the session and purges cached event files plus the saved calendar selection. The expired-credentials path (fired when a Google call fails after a revoke or a non-network refresh failure) is a light reset: it clears the dead session so the sign-in prompt reappears but deliberately keeps cached events and calendar selection, so re-consenting after a revoke or password change doesn't force a full re-setup.