Skip to main content
Gmail authorization in PayPulse is initiated entirely on the iOS device. The backend never participates in the OAuth authorization code exchange — it only receives the resulting tokens.

Google Sign-In SDK

The iOS app uses the Google Sign-In SDK for iOS. The SDK handles:
  • Presenting the native Google consent screen.
  • Managing the OAuth authorization code exchange with Google’s servers.
  • Returning an access token, optional refresh token, and token metadata to the app.
The app is configured with an iOS client ID from Google Cloud Console. Because this is a native mobile client, it is classified as a public OAuth client — no client secret is required or used.
iOS OAuth credentials (client ID) are separate from server-side OAuth credentials. The iOS client ID is stored in AWS Secrets Manager under Google-OAuth-Client-ID and is read by Lambda functions when they need to refresh tokens. No client secret exists for the iOS client; Google’s token endpoint accepts refresh requests from public clients without one.

Native iOS OAuth flow

1

User initiates Gmail connection

The user taps the “Connect Gmail” button in the PayPulse app. The app calls GIDSignIn.sharedInstance.signIn(...) with the gmail.readonly scope.
2

Google consent screen

The Google Sign-In SDK opens a secure in-app browser session (ASWebAuthenticationSession) displaying Google’s consent screen. The user reviews the requested permissions and approves.
3

Tokens returned to the app

Google’s servers exchange the authorization code for tokens and return them to the SDK. The app receives an access_token, a refresh_token (on first authorization), expires_in, and the granted scope.
4

Tokens sent to backend

The app immediately sends the tokens to the backend via the store-tokens endpoint.

The /v1/auth/gmail-tokens endpoint

The iOS app posts token data to this endpoint immediately after the user grants consent. The request must include a valid PayPulse JWT in the Authorization header. Method: POST
Path: /v1/auth/gmail-tokens
Authentication: Bearer JWT (from PayPulse login)

Request body

request body
{
  "access_token": "ya29.a0AfB_...",
  "refresh_token": "1//0gABC...",
  "expires_in": 3600,
  "scope": [
    "https://www.googleapis.com/auth/gmail.readonly",
    "https://www.googleapis.com/auth/userinfo.email",
    "https://www.googleapis.com/auth/userinfo.profile"
  ],
  "email": "user@gmail.com"
}
FieldTypeRequiredDescription
access_tokenstringYesShort-lived OAuth access token from Google Sign-In
refresh_tokenstringNoLong-lived refresh token; present on first authorization
expires_inintegerNoToken lifetime in seconds (defaults to 3600)
scopestring[]NoArray of granted OAuth scopes
emailstringNoUser’s Google email address
The endpoint also accepts application/x-www-form-urlencoded bodies for compatibility.

Response

success response (201)
{
  "message": "Gmail OAuth tokens stored successfully!",
  "data": {
    "google_email": "user@gmail.com",
    "scope": "https://www.googleapis.com/auth/gmail.readonly ...",
    "account_switch": false,
    "message": "First Gmail connection"
  }
}
The account_switch field is true when the user connects a different Google account than the one previously stored. The iOS app can use this to display a warning.

Gmail connection status in user profile

The GET /v1/user/me endpoint returns a gmail_account_connected boolean field that reflects whether the user has OAuth tokens stored in Secrets Manager:
user profile response
{
  "message": "User profile retrieved successfully",
  "data": {
    "name": "Jane Smith",
    "email": "jane@example.com",
    "created_on": "2024-11-01T10:00:00Z",
    "gmail_account_connected": true
  }
}
The iOS app uses gmail_account_connected to decide whether to show the “Connect Gmail” button or the connected Gmail address. The check is performed by looking up the secret gmail/user/{user_id} in Secrets Manager — if it exists, the account is considered connected.