# `authAutomat` > **Source File**: [`src/automats/authAutomat.js`](file:///home/seb/src/dashfront/src/automats/authAutomat.js) > **Registration Key**: `'auth'` (`Automat.get('auth')` / `window.__AUTOMATS__.get('auth')`) --- ## 1. Overview & Purpose `authAutomat` manages the authentication and session lifecycle of the Dashfront client, as well as tenant company selection. It acts as the root coordinator for all other configuration and catalog automats (`companyAutomat`, `categoryAutomat`, `brandAutomat`, `unitAutomat`, `posterminalAutomat`, `supplierAutomat`). ### Core Responsibilities - **Session Lifecycle**: Handles transitioning through `'loading'` → `'authed'` or `'anon'`. - **Google One Tap / GIS Integration**: Exchanging Google OAuth JWTs for session cookies via `/api/auth/google`. - **Multi-Tenant Company Switching**: Updating the active company context via `/api/auth/selected-company` and synchronizing user state. - **Event Dispatching**: Emits cross-subsystem events for session establishment, company changes, and logout. - **Automatic Sub-Store Bootstrap**: Downstream configuration automats subscribe to `authAutomat` to load or clear themselves reactively. --- ## 2. State Structure ```ts interface AuthUser { id?: string | number; googleAuthId?: string; email?: string; name?: string; picture?: string | null; selectedCompanyId?: string | number | null; companies?: Array<{ id: string | number; name: string }>; [key: string]: any; } interface AuthState { /** Authenticated user profile, or null when anonymous. */ user: AuthUser | null; /** Google OAuth Client ID provided by backend bootstrap. */ googleClientId: string | null; /** Current authentication lifecycle phase. */ status: 'loading' | 'anon' | 'authed'; } ``` ### Initial State ```js const initialState = { user: null, googleClientId: null, status: 'loading', }; ``` --- ## 3. Configuration & Options ```js new Automat(initialState, actions, { name: 'auth' }); ``` - **`name`**: `'auth'` - **`persist`**: `false` (session token resides in secure HTTP-only cookies). --- ## 4. Lifecycle Events & Dispatcher Constants exported from `authAutomat`: - **`AUTH_SESSION_ESTABLISHED = 'auth/sessionEstablished'`**: Fired when a valid session is recognized. - **`AUTH_COMPANY_CHANGED = 'auth/companyChanged'`**: Fired when the active company changes or a new company is created. - **`AUTH_LOGGED_OUT = 'auth/loggedOut'`**: Fired when user logs out. ### Event Dispatcher Registration ```js import { setAuthEventDispatcher } from '../automats/authAutomat'; // Allows routing events to an external system, logger, or telemetry setAuthEventDispatcher((event) => { console.log('[Auth Event]', event.type); }); ``` --- ## 5. Actions & Methods Access actions via `authAutomat.actions.()`. ### `bootstrapSession(session?: object): Promise` Initializes the session on application startup: 1. If `session` is passed directly, uses it; otherwise fetches `GET /api/auth/me`. 2. Updates `googleClientId`. 3. If no user is returned, sets status to `'anon'`. 4. If a user is returned, merges the profile, normalizes base64 avatar pictures, sets status to `'authed'`, and emits `AUTH_SESSION_ESTABLISHED`. ### `loginWithGoogle(credential: string): Promise` Submits a Google One Tap JWT to `POST /api/auth/google`. On success, updates user state, sets status to `'authed'`, and emits `AUTH_SESSION_ESTABLISHED`. ### `selectCompany(companyId: string | number): Promise` Calls `PUT /api/auth/selected-company` to switch active company context in the backend session cookie. Updates `state.user.selectedCompanyId`, triggers `AUTH_COMPANY_CHANGED`, and returns the updated user. ### `createCompany(payload: object): Promise` Creates a new tenant company via `POST /api/company` and automatically selects it for the session. Emits `AUTH_COMPANY_CHANGED`. ### `logout(): Promise` Calls `POST /api/auth/logout`, replaces history state to `'/'`, clears user state, sets status to `'anon'`, and emits `AUTH_LOGGED_OUT`. --- ## 6. Cross-Automat Coordination Downstream automats coordinate with `authAutomat` at module load time. For example: ```js // In companyAutomat.js / brandAutomat.js / unitAutomat.js / posterminalAutomat.js: authAutomat.subscribe((auth) => { const companyId = auth.user?.selectedCompanyId; if (auth.status === 'authed' && companyId) { void targetAutomat.actions.load(); } else if (auth.status === 'anon') { targetAutomat.actions.reset(); } }); ``` When `auth.status` transitions to `'authed'` with a `selectedCompanyId`, all dependent stores load their data. On logout (`'anon'`), they clear their state. --- ## 7. Usage Examples ### A. React `PureComponent` Login Gate / User Menu ```jsx import { PureComponent } from 'react'; import authAutomat from '../automats/authAutomat'; export class UserMenu extends PureComponent { constructor(props) { super(props); this.state = { user: authAutomat.state.user, status: authAutomat.state.status, }; } componentDidMount() { this.unsub = authAutomat.subscribe(this, ['user', 'status']); } componentWillUnmount() { this.unsub?.(); } handleCompanyChange = (e) => { authAutomat.actions.selectCompany(e.target.value); }; handleLogout = () => { authAutomat.actions.logout(); }; render() { const { user, status } = this.state; if (status === 'loading') return
Authentifizierung...
; if (status === 'anon' || !user) return
Nicht angemeldet
; return (
{user.name} ({user.email})
); } } ``` ### B. Application Bootstrap in `AuthBootstrap.jsx` ```js import authAutomat from '../automats/authAutomat'; async function initApp() { try { await authAutomat.actions.bootstrapSession(); } catch (err) { console.error('Session bootstrap failed:', err); } } ``` --- ## 8. Best Practices & Gotchas 1. **Never pass `googleAuthId`**: The frontend must never send `googleAuthId` in request bodies or query params. Identity is extracted from the session cookie on the server. 2. **Handle `'loading'` State**: Do not render authentication-required views while `authAutomat.state.status === 'loading'`. 3. **Picture normalization**: `authAutomat` automatically formats raw base64 picture strings into valid `data:image/...;base64,...` URIs.