Skip to main content

Svelte

If you would like to integrate Scute authentication with your Svelte app, you can do so using the @scute/js-core package. This approach gives you full control over the authentication flow and allows you to build custom UI components that match your app's design.

Head over to the example project repo to check out the complete Svelte example and see how all the components work together.

To get started, install the Scute core SDK with your favorite package manager:

Terminal
npm install @scute/js-core

Add your credentials to your environment variable handler:

VITE_SCUTE_APP_ID="YOUR_SCUTE_PROJECT_ID"
VITE_SCUTE_BASE_URL="YOUR_SCUTE_BASE_URL"

Initialize the Scute client

First initialize the Scute client using the createClient method exposed by @scute/js-core package:

// scute.ts

import { createClient } from "@scute/js-core";

export const scuteClient = createClient({
appId: import.meta.env.VITE_SCUTE_APP_ID,
baseUrl: import.meta.env.VITE_SCUTE_BASE_URL,
});

Create your main App component

Create your main App.svelte component that handles the authentication flow:

<!-- App.svelte -->
<script lang="ts">
import { onMount } from 'svelte';
import {
getMeaningfulError,
SCUTE_MAGIC_PARAM,
SCUTE_SKIP_PARAM,
type ScuteTokenPayload,
type ScuteUserData,
} from "@scute/js-core";
import { scuteClient } from './scute';
import LoginForm from './lib/LoginForm.svelte';
import MagicSent from './lib/MagicSent.svelte';
import MagicVerify from './lib/MagicVerify.svelte';
import OtpForm from './lib/OtpForm.svelte';
import RegisterDevice from './lib/RegisterDevice.svelte';
import Profile from './lib/Profile.svelte';

let identifier = '';
let component = '';
let magicLinkToken: string | null = null;
let tokenPayload: ScuteTokenPayload | null = null;

// Catch magic link token from url if it exists and verify it
// OAuth token is also a magic link token and will be handled by this block.
onMount(() => {
const token = scuteClient.getMagicLinkToken();
if (token) {
component = 'magic_verify';
magicLinkToken = token;
return;
}

// Check existing session on app load
const getSession = async () => {
const { data, error } = await scuteClient.getSession();
if (error) {
console.error(error);
}
if (data?.session && data.session.status === "authenticated") {
component = "profile";
} else {
component = "login";
}
};
getSession();
});

function setComponent(newComponent: string) {
component = newComponent;
}

function setIdentifier(newIdentifier: string) {
identifier = newIdentifier;
}

function setTokenPayload(newTokenPayload: ScuteTokenPayload | null) {
tokenPayload = newTokenPayload;
}

function setMagicLinkToken(token: string | null) {
magicLinkToken = token;
}
</script>

<main class="app">
{#if component === "profile"}
<Profile {setComponent} />
{:else if component === "login"}
<LoginForm {scuteClient} {identifier} {setIdentifier} {setComponent} />
{:else if component === "magic_verify"}
<MagicVerify {scuteClient} {magicLinkToken} {setTokenPayload} {setComponent} />
{:else if component === "magic_sent"}
<MagicSent {identifier} />
{:else if component === "register_device"}
<RegisterDevice {scuteClient} {tokenPayload} {setComponent} />
{:else if component === "otp_verify"}
<OtpForm {scuteClient} {identifier} {setComponent} {setTokenPayload} />
{/if}
</main>

<style>
.app {
display: flex;
flex-direction: column;
gap: 32px;
align-items: center;
justify-content: center;
min-height: 100vh;
padding: 2rem;
}
</style>

Create a Login component

Create a LoginForm.svelte component for handling user authentication:

<!-- lib/LoginForm.svelte -->
<script lang="ts">
import { getMeaningfulError, type ScuteClient } from "@scute/js-core";

export let scuteClient: ScuteClient;
export let identifier: string;
export let setIdentifier: (identifier: string) => void;
export let setComponent: (component: string) => void;

async function handleSubmit(e: Event) {
e.preventDefault();
const { data, error } = await scuteClient.signInOrUp(identifier);

if (error) {
console.log("signInOrUp error", getMeaningfulError(error));
return;
}

if (!data) {
// passkey verified.
setComponent("profile");
} else {
if (identifier.includes("@")) {
setComponent("magic_sent");
} else {
setComponent("otp_verify");
}
}
}
</script>

<div class="login-form">
<h1>Sign In</h1>
<form on:submit={handleSubmit}>
<input
type="text"
placeholder="Email or phone number"
bind:value={identifier}
required
/>
<button type="submit">Continue</button>
</form>
</div>

<style>
.login-form {
max-width: 400px;
padding: 2rem;
border: 1px solid #ddd;
border-radius: 8px;
}

input {
width: 100%;
padding: 0.75rem;
margin: 0.5rem 0;
border: 1px solid #ddd;
border-radius: 4px;
}

button {
width: 100%;
padding: 0.75rem;
background: #007bff;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}

button:hover {
background: #0056b3;
}
</style>

Create a Profile component

Create a Profile.svelte component for authenticated users:

<!-- lib/Profile.svelte -->
<script lang="ts">
import { onMount } from 'svelte';
import { type ScuteUserData } from "@scute/js-core";
import { scuteClient } from '../scute';

export let setComponent: (component: string) => void;

let user: ScuteUserData | null = null;

onMount(() => {
const getSession = async () => {
const { data, error } = await scuteClient.getSession();
if (error) {
console.error(error);
}
if (!data?.session || data.session.status === "unauthenticated") {
setComponent("login");
} else {
user = data.user;
}
};
getSession();
});

async function handleSignOut() {
await scuteClient.signOut();
setComponent("login");
}
</script>

{#if user}
<div class="profile">
<h1>Welcome!</h1>
<p>User ID: {user.id}</p>
<p>Email: {user.email || 'N/A'}</p>
<p>Phone: {user.phoneNumber || 'N/A'}</p>
<button on:click={handleSignOut}>Sign Out</button>
</div>
{:else}
<div>Loading...</div>
{/if}

<style>
.profile {
max-width: 400px;
padding: 2rem;
border: 1px solid #ddd;
border-radius: 8px;
text-align: center;
}

button {
padding: 0.75rem 1.5rem;
background: #dc3545;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
margin-top: 1rem;
}

button:hover {
background: #c82333;
}
</style>

Additional Components

The example also includes additional components for handling different authentication flows:

  • MagicVerify.svelte - For verifying magic link tokens
  • MagicSent.svelte - Shows confirmation when magic link is sent
  • OtpForm.svelte - For OTP verification (SMS/phone)
  • RegisterDevice.svelte - For WebAuthn device registration

You can find the complete implementation of these components in the example repository.

Running the Example

  1. Clone the example repository
  2. Copy env.example to .env and add your credentials
  3. Install dependencies: npm install
  4. Start the development server: npm run dev

Congrats! You now have a working Scute authentication system in your Svelte app!

Core Methods

The scuteClient provides several methods for authentication:

signInOrUp(identifier: string)

Initiates the sign-in or sign-up process with an email or phone number.

getSession()

Retrieves the current user session and user data.

signOut()

Signs out the current user and clears the session.

getMagicLinkToken()

Extracts magic link tokens from the current URL for verification.

verifyMagicLink(token: string)

Verifies a magic link token and completes authentication.

For more methods and detailed API documentation, check out the type docs.