
Company News
/Security News
Socket Selected for OpenAI's Cybersecurity Grant Program
Socket is an initial recipient of OpenAI's Cybersecurity Grant Program, which commits $10M in API credits to defenders securing open source software.
@akcodeworks/padlock
Advanced tools
Simple OAuth + trusted-provider auth for SvelteKit.
IMPORTANT: This package is in early development and is not ready for production use. Using it may result in data loss or a breach.
bun add @akcodeworks/padlock
# or
npm i @akcodeworks/padlock
/auth and /auth/callback.Create a Padlock class instance. Important: this module is server-only; never import it in the browser or any client-side code.
src/lib/server/padlock.ts
import { env } from '$env/dynamic/private';
import { Padlock, type PadlockConfig } from '@akcodeworks/padlock';
const config = {
baseUrl: 'http://localhost:5173',
jwt: {
secret: env.AUTH_SECRET,
cookie: {
name: 'padlock_jwt',
httpOnly: true,
sameSite: 'lax',
secure: false
}
},
providers: {
github: {
clientId: env.GITHUB_CLIENT_ID,
clientSecret: env.GITHUB_CLIENT_SECRET,
scopes: ['repo']
}
},
callbacks: {
async onUser(user) {
// Optional: map or persist user here
const saved = await db.user.upsert({
where: { providerAccountId: user.providerAccountId },
create: {
provider: user.provider,
providerAccountId: user.providerAccountId,
email: user.email,
name: user.name,
avatar: user.avatar
},
update: {
email: user.email,
name: user.name,
avatar: user.avatar
}
});
// Note: do not redirect here or the auth cookie will not be saved.
return { ...user, raw: { ...user.raw, dbId: saved.id } };
},
async onError(err) {
console.error(err);
}
}
} satisfies PadlockConfig;
export const padlock = new Padlock(config);
Built-in provider example (GitHub is supported out of the box): see the Supported providers section for defaults and optional scopes.
This handles the initial OAuth redirect and trusted-provider POST auth.
This endpoint must be located at src/routes/auth/+server.ts.
src/routes/auth/+server.ts
import { padlock } from '$lib/server/padlock';
export const GET = padlock.auth();
export const POST = padlock.auth();
This handles the OAuth provider redirect.
This endpoint must be located at src/routes/auth/callback/+server.ts.
src/routes/auth/callback/+server.ts
import { padlock } from '$lib/server/padlock';
export const GET = padlock.callback();
Import from @akcodeworks/padlock/client so server-only code never bundles into the
browser.
src/routes/+page.svelte
<script lang="ts">
import { createSignin } from '@akcodeworks/padlock/client';
type AuthProviders = {
github: [];
internal: [email: string, password: string];
};
const signin = createSignin<AuthProviders>();
</script>
<button onclick={() => signin('github')}> Sign in with GitHub </button>
<button onclick={() => signin('internal', 'email', 'pass')}> Sign in with Internal </button>
Trusted providers let you authenticate via your own backend logic, such as
email/password. Use trustedProvider<TRaw>() to describe the shape of
user.raw that you return from authenticate(). This is the custom data
you want to keep on the user object (role, db id, etc.). The authenticate
function args are inferred from your implementation, so createSignin
and padlock.signin() can be typed automatically.
Your authenticate() must always return this exact shape. If auth fails,
return null or throw an error.
type OAuthUser<TRaw = unknown> = {
provider: string;
providerAccountId: string;
email: string | null;
name: string | null;
avatar: string | null;
raw: TRaw;
};
src/lib/server/padlock.ts
import { trustedProvider, Padlock } from '@akcodeworks/padlock';
const internal = trustedProvider<{ id: string; role: 'admin' | 'user' }>()({
async authenticate(email: string, password: string) {
// Example DB lookup (replace with your DB client)
const user = await db.user.findUnique({ where: { email } });
if (!user) {
throw new Error('invalid credentials');
}
const isValid = await verifyPassword(password, user.passwordHash);
if (!isValid) {
throw new Error('invalid credentials');
}
return {
provider: 'internal',
providerAccountId: String(user.id),
email: user.email,
name: user.name ?? null,
avatar: user.avatarUrl ?? null,
raw: { id: String(user.id), role: user.role }
};
}
});
export const padlock = new Padlock({
baseUrl: 'http://localhost:5173',
trustedProviders: {
internal
}
});
Use authorize() in server load functions or endpoints.
src/routes/dashboard/+page.server.ts
import { padlock } from '$lib/server/padlock';
export async function load(event) {
const user = await padlock.authorize(event, { required: true });
return { user };
}
Or in hooks.server.ts to enforce auth globally or on selected routes:
src/hooks.server.ts
import { padlock } from '$lib/server/padlock';
import { redirect, type Handle } from '@sveltejs/kit';
export const handle: Handle = async ({ event, resolve }) => {
if (event.url.pathname.startsWith('/dashboard')) {
const user = await padlock.authorize(event);
if (!user) {
throw redirect(303, '/');
}
}
return resolve(event);
};
new Padlock({ ... }) accepts:
baseUrl (required): Base URL of your app, used for OAuth redirects.providers: OAuth providers configuration.trustedProviders: custom auth providers (optional).jwt: JWT cookie configuration.callbacks.onUser: called after provider auth.callbacks.onError: called on auth errors.onInvalidConfiguration: "silent" | "warn" | "error".providers: {
github: {
clientId: "...",
clientSecret: "...",
scopes: ["repo"],
redirectUri: "https://your.app/auth/callback"
}
}
Notes:
scopes is joined with spaces for the OAuth request.redirectUri overrides the default ${baseUrl}/auth/callback.satisfies PadlockConfig
(it will catch unknown fields like scope).Provider responses:
raw contains the provider's response payload (including any extra fields
granted by your chosen scopes) so you can fall back to provider-specific data
when it isn't mapped onto the normalized fields.jwt: {
secret: "your-secret",
expiresInSeconds: 3600,
cookie: {
name: "padlock_jwt",
httpOnly: true,
sameSite: "lax",
secure: true,
path: "/"
}
}
If jwt is set, Padlock signs a token and stores it in a cookie. It does
not return the token in the response body.
auth()Returns a SvelteKit RequestHandler. Use for /auth to:
authenticate function.
The handler returns a JSON response with the normalized user. If jwt is
configured, it also sets the JWT cookie.// src/routes/auth/+server.ts
import { padlock } from '$lib/server/padlock';
export const GET = padlock.auth();
export const POST = padlock.auth();
callback()Returns a SvelteKit RequestHandler for /auth/callback. This:
// src/routes/auth/callback/+server.ts
import { padlock } from '$lib/server/padlock';
export const GET = padlock.callback();
authorize(event, options)Validates the JWT from either the Authorization header or cookie and
returns the payload. If required is true, it throws a 401 when missing
or invalid. The payload includes sub, provider, and providerAccountId.
If required is false (default), it returns null when no valid token
is present.
const payload = await padlock.authorize(event, { required: true });
signin()Returns a typed client helper for OAuth/trusted signin. Use it in the
browser; do not call on the server. Prefer the padlock/client entry. If
called with only a provider name, it triggers OAuth redirect. If called
with args, it sends a POST for trusted-provider auth.
If you do not want to use the helper, you can implement the same behavior
manually. The provider name must match the keys you configured on the
Padlock instance (providers and trustedProviders):
// From your Padlock config:
// providers: { github: { ... } }
// trustedProviders: { internal: { ... } }
// OAuth redirect (no args)
window.location.href = `/auth?provider=${encodeURIComponent('github')}`;
// Trusted provider POST (args are whatever your provider expects)
await fetch(`/auth?provider=${encodeURIComponent('internal')}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ args: ['email@example.com', 'password'] })
});
// In a Svelte component
import { createSignin } from '@akcodeworks/padlock/client';
const signin = createSignin<{ github: []; internal: [string, string] }>();
The following providers are supported right now. Padlock adds the required
scopes by default to ensure OAuthUser fields are populated; you can extend
them via the scopes array in your config.
GitHub
read:user, user:emailExample:
providers: {
github: {
clientId: env.GITHUB_CLIENT_ID,
clientSecret: env.GITHUB_CLIENT_SECRET,
scopes: ["repo"]
}
}
Microsoft
openid, profile, email, User.ReadTenant example:
providers: {
microsoft: {
clientId: "...",
clientSecret: "...",
scopes: ["Calendars.Read"],
allowedTenants: ["00000000-0000-0000-0000-000000000000"]
}
}
Note: if allowedTenants is omitted or empty, the Microsoft provider uses the
common endpoint, so your Azure app registration must allow multi-tenant
sign-ins.
Origin or Referer.@akcodeworks/padlock (server entry) in browser code. Use
@akcodeworks/padlock/client.jwt.cookie.secure = true.redirectUri, it must exactly match the provider config.src/routes/auth/callback/+server.ts and your provider redirect
URL matches ${baseUrl}/auth/callback (or your redirectUri override).baseUrl. Align baseUrl with the actual site origin.Padlock from @akcodeworks/padlock, and
createSignin from @akcodeworks/padlock/client.onUser and ensure your
cookie secure flag matches the scheme (HTTPS in production). Also make
sure you set jwt in the Padlock config.bun run dev
Package locally:
bun run pack:clean
FAQs
A simple OAuth2 provider for SvelteKit
The npm package @akcodeworks/padlock receives a total of 7 weekly downloads. As such, @akcodeworks/padlock popularity was classified as not popular.
We found that @akcodeworks/padlock demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Company News
/Security News
Socket is an initial recipient of OpenAI's Cybersecurity Grant Program, which commits $10M in API credits to defenders securing open source software.

Security News
Socket CEO Feross Aboukhadijeh joins 10 Minutes or Less, a podcast by Ali Rohde, to discuss the recent surge in open source supply chain attacks.

Research
/Security News
Campaign of 108 extensions harvests identities, steals sessions, and adds backdoors to browsers, all tied to the same C2 infrastructure.