You can get up and running with Appwrite Authentication in minutes. You can add basic email and password authentication to your app with just a few lines of code.
Signup
You can use the Appwrite Client SDKs to create an account using email and password.
import { Client, Account, ID } from "appwrite";
const client = new Client()
.setEndpoint('https://cloud.appwrite.io/v1') // Your API Endpoint
.setProject('<PROJECT_ID>'); // Your project ID
const account = new Account(client);
const user = await account.create(
ID.unique(),
'[email protected]',
'password'
);
Login
After you've created your account, users can be logged in using the Create Email Session method.
const session = await account.createEmailPasswordSession(
email,
password
);
Check authentication state
After logging in, you can check the authentication state of the user.
Appwrite's SDKs are stateless, so you need to manage the session state in your app. You can use the Get Account method to check if the user is logged in.
try {
const user = await account.get();
// Logged in
} catch (err) {
// Not logged in
}
Navigation (Optional)
A common pattern is to use route guards to redirect users to the login page if they are not authenticated. You can check the authentication state on app launch and before entering a protected route by calling get().
Route guard implementations are opinionated and depend on the platform and frame you are using. Take a look at some example usages from different platforms as inspiration.
Web frameworks
Before routing to a page, you can check if the user is logged in and redirect them to the login page if they are not.
You can use React router loaders to check if the user is logged in before rendering a route.
You can find a similar example in this YouTube video.
Webimport * as React from "react"; import { createBrowserRouter, } from "react-router-dom"; import "./index.css"; import Login from "./Login"; import Protected from "./Protected"; import { account } from "./lib/appwrite"; import { redirect } from "react-router-dom"; const router = createBrowserRouter([ { path: "/protected", element: <Protected />, loader: async () => { try{ // logged in? pass user to the route const user = await account.get(); return { user }; } catch { // not logged in? redirect to login throw redirect('/login') } } }, { path: "/login", element: <Login />, }, ]); export default router;
You can use Vue router wiht a Pinia store to check if the user is logged in before rendering a route.
First, create a simple Pinia store to manage the authentication state.
Webimport { account, ID, type Models } from '@/lib/appwrite' import type { register } from 'module'; import { defineStore } from 'pinia' export const useAuthStore = defineStore({ id: 'auth', state: () => ({ user: null as null | Models.User<Models.Preferences>, }), getters: { isLoggedIn(): boolean { return !!this.user; }, }, actions: { async init() { try { this.user = await account.get(); } catch (error) { this.user = null; } }, // ... other operations }, })
Then, check the authentication state before routing to a protected route.
Webimport { createApp } from 'vue' import { createPinia } from 'pinia' import App from './App.vue' import router from './router' import { useAuthStore } from './stores/auth' const app = createApp(App) app.use(createPinia()) const auth = useAuthStore(); auth.init().then(() => { router.beforeEach((to, from, next) => { // Not logged in? if (to.name == 'protected' && auth.isLoggedIn == false) { // Redirect to login if going to a protected route next({ name: 'login' }) } else { next() } }) app.use(router) app.mount('#app') })
Webimport { Injectable } from '@angular/core'; import { ActivatedRouteSnapshot, CanActivate, RouterStateSnapshot, UrlTree, Router } from '@angular/router'; import { Observable, from, of } from 'rxjs'; import { catchError, map } from 'rxjs/operators'; import { account } from './lib/appwrite'; @Injectable({ providedIn: 'root' }) export class AuthGuardGuard implements CanActivate { constructor(private router: Router) {} canActivate( route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree { if (route.routeConfig?.path === "protected") { return this.checkLogin(); } return of(true); } private checkLogin(): Observable<boolean | UrlTree> { return from(account.get()).pipe( map(() => true), catchError(() => of(this.router.createUrlTree(['/login']))) ); } }
In the root level +layout.svelte file, you can check the authentication state before rendering a route.
Web// src/routes/+layout.js import { appwrite } from "$lib/appwrite"; // Turn off SSR globally, turning the project into a static site export const ssr = false; export const load = async () => { try { return { account: await appwrite.account.get(), }; } catch { return { account: null, }; } };
This will be accessible in the load function of each child route.
Web// src/routes/protected/+page.js import { redirect } from '@sveltejs/kit'; /** @type {import('./$types').PageLoad} */ export async function load({ parent }) { const { account } = await parent(); if (!account) { throw redirect(303, '/login'); } }
Mobile and native
With mobile apps, you can apply similar logic to check the authentication state before displaying a screen or view.
This example uses the Flutter Go router as an example, but the same concepts apply to other routing libraries.
First, create a ChangeNotifier to manage the authentication state.
Flutterimport 'package:flutter/material.dart'; import 'package:appwrite/appwrite.dart' show Client, ID; import 'package:appwrite/appwrite.dart' as Appwrite; import 'package:appwrite/models.dart' show User; class Account extends ChangeNotifier { final Appwrite.Account _account; User? _user; User? get user => _user; Account(Client client) : _account = Appwrite.Account(client); Future<void> init() async { try { _user = await _account.get(); notifyListeners(); } catch(e) { debugPrint(e.toString()); rethrow; } } // ... other operations }
You can then use this state to redirect users to the login page if they are not authenticated.
Flutterimport 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:provider/provider.dart'; import './providers/account.dart'; import './pages/login.dart'; import './pages/protected.dart'; String Function(BuildContext context, GoRouterState state) redirect = (BuildContext context, GoRouterState state) => context.read<Account>().user == null && state.matchedLocation != '/login' ? '/login' : state.matchedLocation; final router = GoRouter( redirect: redirect, initialLocation: '/login', routes: [ GoRoute( path: '/login', pageBuilder: (context, state) => const MaterialPage(child: LoginPage()), ), GoRoute( path: '/protected', pageBuilder: (context, state) => const MaterialPage(child: ProtectedPage()), ) ], );
For Apple platforms, this example uses a NavigationStack but you can use similar concepts with other navigation methods.
Initialize Appwrite and create an AppwriteService.
Appleimport Foundation import Appwrite import AppwriteModels import JSONCodable class Appwrite { var client: Client var account: Account var databases: Databases let databaseId = "default" let collectionId = "ideas-tracker" public init() { self.client = Client() .setEndpoint("https://cloud.appwrite.io/v1") .setProject("<PROJECT_ID>") self.account = Account(client) } public func getUser() async throws -> User<[String: AnyCodable]> { let user = try await account.get() // you can also store the user in a local store return user } }
On launch, you can display a SplashView while you verify the authentication state.
Appleimport Foundation import SwiftUI struct SplashView: View { @EnvironmentObject private var router: Router @EnvironmentObject private var AppwriteService: AppwriteService var body: some View { NavigationStack(path: $router.routes) { VStack { Text("Example App") .font(.largeTitle) .fontWeight(.bold) .padding() }.task { let user = await self.AppwriteService.getUser(); if !user { router.pushReplacement(.login) } else { router.pushReplacement(.protected) } } .navigationDestination(for: Route.self, destination: { $0 }) } } }
In your router, you can also check the authentication state before rendering a route.
Applefinal class Router: ObservableObject { @Published var routes = [Route]() func push(_ screen: Route) { // Make sure you've already stored the user and auth state in a local store if (screen == .protected && !isLoggedIn){ routes.append(.login) } routes.append(screen) } // ... other operations }
Create some Appwrite Service, for example, AppwriteService to manage the authentication state. You can find a version of this example in the Appwrite Android tutorial.
Android (Kotlin)//... imports object Appwrite { private const val ENDPOINT = "https://cloud.appwrite.io/v1" private const val PROJECT_ID = "<PROJECT_ID>" private lateinit var client: Client fun init(context: Context) { client = Client(context) .setEndpoint(ENDPOINT) .setProject(PROJECT_ID) } }
Then, create an auth service to manage the authentication state.
Android (Kotlin)//... imports class AccountService(client: Client) { private val account = Account(client) suspend fun getLoggedIn(): User<Map<String, Any>>? { return try { account.get() } catch (e: AppwriteException) { null } } // ... other operations }
Wrap your routes in some view, for example, AppContent, to check the authentication state before rendering a route.
Android (Kotlin)@Composable private fun AppContent(accountService: AccountService) { val user = remember { mutableStateOf<User<Map<String, Any>>?>(null) } val screen = remember { mutableStateOf(Screen.Protected) } LaunchedEffect(screen) { user.value = accountService.getLoggedIn() } Scaffold(bottomBar = { AppBottomBar(screen) }) { padding -> Column(modifier = Modifier.padding(padding)) { when (screen.value) { Screen.User -> LoginScreen(user, accountService) else -> ProtectedScreen(user.value) } } } }
In the MainActivity class, initialize the Appwrite service and display the AppContent based on the authentication state.
Android (Kotlin)// ...imports In the `MainActivity` class, initialize the Appwrite service. class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) Appwrite.init(applicationContext) setContent { // Update this line 👇 AppContent(Appwrite.account) } } }
This example will use @react-navigation/native and @react-navigation/native-stack to manage the authentication state and redirect users to the login page if they are not authenticated. You can find a version of this example in the Appwrite Android tutorial.
Create a UserContext to manage the authentication state.
React Nativeimport { StyleSheet, Text, View } from 'react-native'; import { UserProvider } from './contexts/UserContext'; import { Router } from './lib/Router'; export default function App() { return ( <UserProvider> <Router /> </UserProvider > ); }
Then, consume the UserContext in your Router component to check the authentication state before rendering a route.
React Nativeimport { NavigationContainer } from '@react-navigation/native'; import LoginScreen from '../views/Login'; import ProtectedSCreen from '../views/Protected'; import { createNativeStackNavigator } from '@react-navigation/native-stack'; import { useUser } from '../contexts/UserContext'; const Stack = createNativeStackNavigator(); export function Router() { const user = useUser(); return ( <NavigationContainer> <Stack.Navigator> {user.current == null ? ( <Stack.Screen name="Login" component={LoginScreen} options={{ title: 'Login' }} /> ) : ( <Stack.Screen name="Protected" component={ProtectedSCreen} options={{ title: 'Protected' }} /> )} </Stack.Navigator> </NavigationContainer> ); }