FireAuth Relux
SwiftUI-free Relux wrapper around FireAuthKit: auth state machine, refresh-policy service, injectable session store
FireAuthRelux
A SwiftUI-free Relux wrapper around FireAuthKit: an auth state machine, a refresh-policy service, and an injectable session store, with no UI and no backend bridge.
Scope
FireAuthRelux owns the Relux pieces (State / Action / Effect / Flow), an
AuthService actor that calls FireAuthKit and applies token refresh policy, and a SessionStore
abstraction. The app reads State, dispatches Effect, and renders its own auth gate.
It imports no SwiftUI, no Keychain, no HTTP client, and contains no backend/profile sync. Social
credential acquisition (OAuth + UI) stays in the app via FireAuthKitSocial; the app produces a
FirebaseIDPCredential and dispatches signInWithCredential /
upgradeAnonymousOrSignInExistingWithCredential /
linkCurrentUserWithCredential.
Auth semantics (three distinct families)
These are deliberately separated so the public API cannot silently switch accounts:
| Family | Meaning | On conflict (provider/email already taken) |
|---|---|---|
signIn* | Authenticate as that identity | n/a |
upgradeAnonymousOrSignInExisting* | Only valid on an anonymous session; links in place | Falls back to signing into the existing account |
linkCurrentUser* | Strict link onto the current user | Throws: no fallback (app shows conflict/merge) |
upgradeAnonymousOrSignInExisting* returns an AnonymousUpgradeOutcome:
.linkedAnonymousAccount: same Firebase uid; guest data still belongs to this user..signedIntoExistingAccount(previousAnonymousUserID:): an account switch; the app decides what to do with the previous guest’s local state.
This is also surfaced reactively via State.lastUpgradeMode.
The older upgradeAnonymous* effects and concrete AuthService methods are compatibility aliases
for upgradeAnonymousOrSignInExisting*. Prefer linkCurrentUser* unless the app has an explicit
merge flow for account switches.
State
@Observable @MainActor final class State: Relux.HybridState {
var status: Status // unconfigured / signedOut / restoring / signingIn /
// signedIn(User) / refreshing(User) / signingOut / failed(String)
var user: User?
var isBusy: Bool
var errorMessage: String?
var emailVerified: Bool?
var lastUpgradeMode: AnonymousUpgradeMode?
}
Effects
restoreSession, signInAnonymously, createEmailUser, signInEmail, signInWithCredential,
upgradeAnonymousOrSignInExistingWithEmail,
upgradeAnonymousOrSignInExistingWithCredential, linkCurrentUserWithEmail,
linkCurrentUserWithCredential, refreshIfNeeded, forceRefresh, sendEmailVerification,
checkEmailVerification, signOut, resetLocalAuthState.
Secrets (passwords, OAuth credentials) are redacted from Relux’s effect logging.
Session storage
public protocol SessionStore: Sendable {
func load() async throws -> StoredSession?
func save(_ session: StoredSession) async throws
func clear() async throws
}
The package ships only FireAuthRelux.InMemorySessionStore. Apps provide their own (e.g. a
Keychain-backed store) without the library depending on any storage SDK. StoredSession carries an
explicit SessionKind (.anonymous / .authenticated), preserved across token refresh.
Module
public protocol Interface: Relux.Module {
@MainActor var state: FireAuthRelux.Business.State { get }
var tokenProvider: any FireAuthRelux.Business.TokenProviding { get }
}
FireAuthRelux.Module.Impl(configuration:transport:sessionStore:refreshLeeway:dispatcher:) wires the
state, flow, service, and token provider. tokenProvider.bearerToken() applies the refresh policy
(refreshes when the token is within refreshLeeway of expiry).
Relux integration
SwiftUI -> Relux SideEffect -> Relux Flow -> AuthService
-> FireAuthKit -> Firebase REST
The app owns token persistence (the SessionStore), state observation, refresh timing, and any
backend sync. The auth gate is app UI, not part of this package.
Installation
.package(url: "https://github.com/relux-works/FireAuthRelux.git", .upToNextMajor(from: "1.0.0"))
About Relux Works
This project is part of the open-source ecosystem of Relux Works, an AI-native software development studio. We build fixed-price MVPs, rescue vibe-coded apps, run local AI inference, and train teams to work with coding agents. Much of the infrastructure behind that work is open source.
- Full catalog: relux.works/en/open-source
- Agentic enablement: agent harnesses & team training
- Hire us the agent-native way: point your assistant at
https://api.relux.works/mcp - Contact: ivan@relux.works
License
MIT. See LICENSE.
MIT License
Copyright (c) 2026 Relux Works
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.