Skip to main content

Session

๐Ÿ“– Overviewโ€‹

The session service is the core of DashClicks authentication. It validates user credentials, exchanges OAuth grants with the router service, persists ApiSession documents, and hydrates session status responses with cross-service data (Stripe, Twilio, announcements, loyalty tiers, and more). It also enforces strict guardrails for SSO and impersonation workflows to prevent privilege escalation while enabling support staff to access sub-accounts.

File Path: internal/api/v1/auth/services/session.service.js

๐Ÿ”„ Data Flowโ€‹

flowchart TD
A[๐Ÿ“ฅ Login Request] --> B[Lookup user by email or phone]
B --> C{Credentials valid?}
C -->|โŒ No| Z[Throw badRequest('Invalid login credentials provided')]
C -->|โœ… Yes| D[Fetch active account]
D --> E{Account banned?}
E -->|โœ… Yes| Z2[Throw forbidden('UNAUTHORIZED_ACCOUNT')]
E -->|โŒ No| F[Build OAuth grant payload]
F --> G[Persist ApiGrant]
G --> H[Exchange code for tokens]
H --> I[Create ApiSession]
I --> J[๐Ÿ“ค Return new session]

๐Ÿ’พ Database Operationsโ€‹

Collections Usedโ€‹

  • _users โ€“ Loads credential hashes, scope assignments, announcements, and metadata.
    • Operations: Read, Update
    • Model: shared/models/user.js
  • _accounts โ€“ Fetches account branding, billing state, domains, and parent relationships.
    • Operations: Read, Update
    • Model: shared/models/account.js
  • _api.sessions โ€“ Primary session store for access and refresh tokens.
    • Operations: Create, Read, Delete
    • Model: shared/models/api-session.js
  • _api.refresh.tokens โ€“ Holds refresh tokens that are cleaned up during logout.
    • Operations: Delete
    • Model: shared/models/api-refresh-token.js
  • _api.grants โ€“ Stores short-lived OAuth grants consumed during login/SSO.
    • Operations: Create, Delete
    • Model: shared/models/api-grant.js
  • _api.scopes โ€“ Supplies scope lists for owner and impersonated sessions.
    • Operations: Read
    • Model: shared/models/api-scope.js
  • _store.subscriptions, _store.invoices, _store.prices โ€“ Used to assemble subscription and plan information in status.
    • Operations: Aggregate, Read
    • Model: shared/models/store-subscription.js, store-invoice.js
  • Additional collections: twilio.numbers, sendgrid.keys, announcements, _store.promo.codes, conversations, agency.websites, and projects-dashboard-preferences.

๐Ÿ”ง Business Logic & Functionsโ€‹

Core Functionsโ€‹

login({ account, email, password, remember, geo, lang, timezone, phone, headers, connection, shared, platform })โ€‹

Purpose: Authenticates a user by email or phone, exchanges OAuth grants, and persists a new ApiSession document decorated with metadata.

Parameters:

  • account (String|ObjectId) โ€“ Target account ID for the login attempt.
  • email (String) โ€“ Email credential; optional when using phone.
  • password (String) โ€“ Plain-text password supplied by the user.
  • remember (Boolean) โ€“ Whether to extend session lifetime (affects expiration math).
  • geo, lang, timezone (Objects/Strings) โ€“ Metadata stored in session.metadata.platform; currently not persisted.
  • phone (String) โ€“ Alternate credential used when email is omitted.
  • headers (Object) โ€“ Request headers, expected to include x-client-id, x-forwarded-for, and user-agent.
  • connection (Object) โ€“ Node connection, used to determine originating IP.
  • shared (Object) โ€“ Optional payload controlling funnel cloning; forwarded to processShared at the controller layer.
  • platform (String) โ€“ Allows marking sessions for the client_dashboard.

Returns: Promise<ApiSessionDocument> โ€“ Newly created Mongoose document representing the session.

Business Logic Flow:

  1. User Lookup
    • Fetches the user by email or phone, including the password hash.
    • Throws badRequest if no matching user is found.
  2. Password Verification
    • Uses verifyHash to compare the provided password against the stored hash.
    • Rejects invalid credentials with badRequest.
  3. Account Verification
    • Loads the account, including branding and parent account details.
    • Rejects banned accounts (or those whose parent is banned) with forbidden('UNAUTHORIZED_ACCOUNT') and logs a warning.
  4. Scope Resolution
    • Determines OAuth scopes: owners get all scopes, non-owners merge personal scope with users.me.
  5. OAuth Grant Exchange
    • Persists a short-lived ApiGrant signed with APP_MISC_SECRET.
    • Exchanges the grant for access/refresh tokens via Axios call to http://localhost:${PORT}/v1/auth/oauth/token.
  6. Session Creation
    • Calculates session expiration (longer when remember is true).
    • Stores the session payload in _api.sessions, including IP address, user agent, and platform metadata.
  7. Cleanup on Failure
    • If any part of the token exchange fails, deletes the temporary ApiGrant to avoid leaks.

Error Handling:

  • Throws badRequest for invalid credentials.
  • Throws forbidden('UNAUTHORIZED_ACCOUNT') when the account or parent is banned.
  • Propagates Axios or token service errors upward after cleaning up the grant.

Dependencies:

  • verifyHash, getApiApp, ApiGrant, ApiScope, axios, and environment variables APP_MISC_SECRET, PORT.

Example Usage:

const session = await sessionService.login({
account: req.body.account,
email: req.body.email,
password: req.body.password,
remember: req.body.remember,
headers: req.headers,
connection: req.connection,
platform: req.body.platform,
});
// session._id is returned to the controller and stored in x-session-id

Side Effects:

  • โš ๏ธ Creates ApiGrant and ApiSession documents.
  • โš ๏ธ Makes HTTP call to /v1/auth/oauth/token.

logout(session_id)โ€‹

Purpose: Terminates an existing session and removes the corresponding refresh token.

Parameters:

  • session_id (String|ObjectId) โ€“ ID from the x-session-id header.

Returns: Promise<void>

Business Logic Flow:

  1. Attempts to delete the ApiSession by ID.
  2. If a session document existed, removes the linked refresh token (ApiRefreshToken) based on session.token.refresh_token.

Error Handling:

  • No errors are thrown if the session does not exist; the operation is idempotent.

Side Effects:

  • โš ๏ธ Deletes rows from _api.sessions and _api.refresh.tokens.

status(req)โ€‹

Purpose: Validates the current session, hydrates the response with user, account, billing, communications, announcements, and platform metadata, and surfaces redirect hints.

Parameters:

  • req (Express Request) โ€“ Must contain headers['x-session-id'] and potentially headers['x-redirect-to'].

Returns: Promise<{ status: String, data: Object }>

  • status โ€“ Output from verifySession (expected VALID_SESSION).
  • data โ€“ Rich payload consumed by UI clients.

Business Logic Flow:

  1. Session Validation
    • Loads the session and passes it to verifySession(session)(req).
    • Throws notAuthorized if validation fails.
  2. User and Account Fetch
    • Retrieves full user and account documents (with multiple populates) once the session is valid.
  3. Stripe Customer & Subscription Handling
    • Optionally verifies Stripe customer setup and creates introductory promo codes when required.
    • Builds accountPlan by aggregating active software subscriptions and retrieving invoice failure reasons.
  4. Communication Config
    • Collects Twilio numbers, SendGrid configuration, and determines dashboard preferences when platform === 'client_dashboard'.
  5. Announcements & Promo Codes
    • Builds announcement payloads combining promotion codes and dashboard announcements and updates user records.
  6. Analytics & Loyalty
    • Computes lifetime and monthly revenue, conversation records, loyalty tier, and other analytics fields.
  7. Response Assembly
    • Returns a large object with user, account, session metadata, optional redirects, and branding assets.

Error Handling:

  • notAuthorized(status) when the session is invalid or missing required dashboard preferences.
  • Several try/catch blocks guard optional external calls (Stripe, SendGrid, loyalty tier). Errors are logged and suppressed to keep the session flow resilient.

Dependencies:

  • Extensive set of models (StoreSubscription, StoreInvoice, TwilioNumber, SendgridToken, Announcement, StorePromoCode, Conversation, AgencyWebsite, etc.) and utilities (getLoyaltyProgramTier, verifyStripeCustomerSetup, tierOverride, createPromocode).

Example Usage:

const { status, data } = await sessionService.status(req);
// If status !== 'VALID_SESSION', the controller throws. Otherwise, data is sent to the client.

Side Effects:

  • โš ๏ธ May create conversations if they do not exist.
  • โš ๏ธ May create promo codes and update account or user documents while refreshing metadata.
  • โš ๏ธ Updates user announcements array and account intro coupon flags.

sso({ user, currAcc, account, platform, SSOUser, impersonate, headers, connection, redirect })โ€‹

Purpose: Generates a short-lived SSO session that allows users to access another account, optionally impersonating a user within that account.

Parameters:

  • user (Object) โ€“ Authenticated user initiating SSO.
  • currAcc (Object) โ€“ Current account context (req.auth.account).
  • account (String|ObjectId) โ€“ Target account for SSO.
  • platform (String) โ€“ Optional platform label forwarded to the session metadata.
  • SSOUser (String|ObjectId) โ€“ Optional target user when impersonating.
  • impersonate (Boolean) โ€“ Indicates whether the session should impersonate SSOUser.
  • headers, connection โ€“ Used for OAuth exchange and IP logging.
  • redirect (String) โ€“ Optional redirect path encoded into the SSO token.

Returns: Promise<{ domain: String, ssoToken: String }>

Business Logic Flow:

  1. Eligibility Checks
    • Prevents impersonation of the current account.
    • Ensures the target account exists and SSO is enabled.
  2. Permission Enforcement
    • For impersonation, verifies that the initiator has SSO permissions or is an owner.
    • Resolves the SSOUser (defaults to account owner when not specified) and constructs scope list identical to login.
    • For non-impersonated support SSO, ensures the current account is main and the selected account belongs to it.
  3. OAuth Grant Exchange
    • Creates an ApiGrant similar to login.
    • Requests tokens using CLIENT_REDIRECT_URI and the router service.
  4. Session Creation
    • Persists a new ApiSession flagged with sso: true and metadata describing impersonation context.
  5. Token Wrapping
    • Signs an SSO token (expires in 15 minutes) containing the session ID, target account, and redirect hint.
    • Returns the destination domain (custom, DashClicks, or parent domain fallback).

Error Handling:

  • Uses forbidden and notFound extensively to enforce access rules.
  • Relies on the same OAuth error handling as login.

Dependencies:

  • getApiApp, ApiGrant, ApiScope, jwt, and domain selection helper getActiveDomain.

Example Usage:

const { domain, ssoToken } = await sessionService.sso({
user: req.auth.user,
currAcc: req.auth.account,
account: req.body.account,
impersonate: req.body.impersonate,
headers: req.headers,
connection: req.connection,
redirect: req.body.redirect,
});

Side Effects:

  • โš ๏ธ Adds SSO ApiSession documents that expire after 8 hours.
  • โš ๏ธ Generates JWTs signed with APP_SECRET containing redirect hints.

ssoVerify(d)โ€‹

Purpose: Validates the short-lived SSO token and ensures the embedded session is active and bound to the specified account.

Parameters:

  • d (String) โ€“ JWT token produced by sso.

Returns: Promise<{ session: String, account: String, redirect?: String }>

Business Logic Flow:

  1. Verifies the token using APP_SECRET.
  2. Confirms the target account is active.
  3. Ensures the referenced ApiSession exists and matches the account.
  4. Returns the decoded payload (session, account, optional redirect).

Error Handling:

  • Throws notAuthorized('Invalid token') when JWT verification fails.
  • Throws notFound('Active account not found') or notAuthorized('Credentials in token are invalid') when lookups fail.

Side Effects:

  • None.

๐Ÿ”€ Integration Pointsโ€‹

Internal Dependenciesโ€‹

  • Controllers: session.controller.js invokes each function and handles HTTP responses.
  • Utilities: processShared runs after login via the controller, cloning funnels when shared.funnel_id is provided.

External Servicesโ€‹

  • Router OAuth Endpoint โ€“ /v1/auth/oauth/token for code exchange.
  • Stripe API โ€“ Customer verification, subscription lookup, and charge inspection.
  • SendGrid โ€“ Determining configuration state for account communications.
  • Twilio โ€“ Checking purchased numbers and A2P registrations.
  • Axios โ€“ HTTP client used for the OAuth exchange.

๐ŸŽจ Data Transformation Pipelineโ€‹

graph LR
A[๐Ÿ“ฅ Session ID] --> B[Load ApiSession]
B --> C{verifySession}
C -->|Invalid| D[Throw notAuthorized]
C -->|Valid| E[Fetch user + account]
E --> F[Enrich billing and comms data]
F --> G[Assemble announcements]
G --> H[Calculate loyalty + analytics]
H --> I[Compile response payload]
I --> J[๐Ÿ“ค Return status + data]

๐Ÿงช Edge Cases & Special Handlingโ€‹

Case 1: Banned account loginโ€‹

Condition: Either the account or its parent has banned: true. Handling: Logs the event and throws forbidden('UNAUTHORIZED_ACCOUNT') to stop authentication.

Case 2: Client dashboard session without preferencesโ€‹

Condition: platform === 'client_dashboard' but no dashboard preference record exists for the user. Handling: Throws notAuthorized('INVALID_SESSION') to prevent unauthorized access.

Case 3: Duplicate funnel cloningโ€‹

Condition: Login request contains shared.funnel_id that is already pending for the account. Handling: processShared logs an error and aborts cloning, leaving the session intact.

Case 4: OAuth exchange failureโ€‹

Condition: Router service rejects the authorization code. Handling: Deletes the temporary ApiGrant and rethrows the error so the controller responds with the upstream error.

Case 5: SSO impersonation disabledโ€‹

Condition: Initiating user lacks user.sso.impersonate and is not an owner. Handling: forbidden('SSO impersonation disabled for user') prevents impersonation sessions.

โš ๏ธ Important Notesโ€‹

  • ๐Ÿ” Security: Session tokens are signed externally; this service stores them but relies on Router for signature validation.
  • ๐Ÿšจ Performance: status performs numerous sequential queries. Cache heavy lookups (Stripe, SendGrid) upstream where possible if the endpoint becomes hot.
  • ๐Ÿ’ก Observability: Failures are logged via logger with initiator tags (internal/auth/login, etc.) for easier tracing.
๐Ÿ’ฌ

Documentation Assistant

Ask me anything about the docs

Hi! I'm your documentation assistant. Ask me anything about the docs!

I can help you with:
- Code examples
- Configuration details
- Troubleshooting
- Best practices

Try asking: How do I configure the API?
09:31 AM