mirror of
https://github.com/Dan6erbond/sk-auth.git
synced 2025-06-13 01:15:30 +02:00
Merge branch 'main' into feature/discord-provider
This commit is contained in:
commit
9a8ffa9397
@ -1,3 +1,4 @@
|
|||||||
README.md
|
README.md
|
||||||
app/
|
app/
|
||||||
*.cjs
|
*.cjs
|
||||||
|
dist/
|
||||||
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -76,4 +76,4 @@ typings/
|
|||||||
.fusebox/
|
.fusebox/
|
||||||
|
|
||||||
# Build output
|
# Build output
|
||||||
dist/*
|
dist/
|
||||||
|
@ -144,6 +144,10 @@ SvelteKitAuth is inspired by the [NextAuth.js](https://next-auth.js.org/) packag
|
|||||||
|
|
||||||
As it leverages classes and Typescript, the implementation of such providers is very straightforward, and in the future it will even be possible to register multiple SvelteKitAuth handlers in the same project, should the need arise, by leveraging a class-based client and server setup.
|
As it leverages classes and Typescript, the implementation of such providers is very straightforward, and in the future it will even be possible to register multiple SvelteKitAuth handlers in the same project, should the need arise, by leveraging a class-based client and server setup.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
Looking for help? Check out the [example app](./app/) in the repository source. Make something cool you want to show off? Share it with others [in the discussion section](https://github.com/Dan6erbond/sk-auth/discussions/72).
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
🚧 Work in Progress!
|
🚧 Work in Progress!
|
||||||
|
@ -1,2 +1,6 @@
|
|||||||
|
node_modules/
|
||||||
README.md
|
README.md
|
||||||
*.cjs
|
*.cjs
|
||||||
|
.svelte-kit/
|
||||||
|
static/
|
||||||
|
build/
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
"format": "prettier --write --plugin-search-dir=. ."
|
"format": "prettier --write --plugin-search-dir=. ."
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@sveltejs/kit": "next",
|
"@sveltejs/kit": "^1.0.0-next.259",
|
||||||
"@types/prismjs": "^1.16.5",
|
"@types/prismjs": "^1.16.5",
|
||||||
"@typescript-eslint/eslint-plugin": "^4.19.0",
|
"@typescript-eslint/eslint-plugin": "^4.19.0",
|
||||||
"@typescript-eslint/parser": "^4.19.0",
|
"@typescript-eslint/parser": "^4.19.0",
|
||||||
|
@ -1,13 +1,15 @@
|
|||||||
import type { Handle } from "@sveltejs/kit";
|
import type { Handle } from "@sveltejs/kit";
|
||||||
import { appAuth } from "$lib/appAuth";
|
import { appAuth } from "$lib/appAuth";
|
||||||
|
|
||||||
export const handle: Handle = async ({ request, render }) => {
|
export const handle: Handle = async ({ event, resolve }) => {
|
||||||
// TODO https://github.com/sveltejs/kit/issues/1046
|
// TODO https://github.com/sveltejs/kit/issues/1046
|
||||||
if (request.query.has("_method")) {
|
|
||||||
request.method = request.query.get("_method").toUpperCase();
|
|
||||||
|
if (event.request.query.has("_method")) {
|
||||||
|
event.request.method = event.request.query.get("_method").toUpperCase();
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = await render(request);
|
const response = await resolve(event);
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
};
|
};
|
||||||
|
3302
app/yarn.lock
3302
app/yarn.lock
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "sk-auth",
|
"name": "sk-auth",
|
||||||
"version": "0.4.0",
|
"version": "0.4.1",
|
||||||
"description": "Authentication library for use with SvelteKit featuring built-in OAuth providers and zero restriction customization!",
|
"description": "Authentication library for use with SvelteKit featuring built-in OAuth providers and zero restriction customization!",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"module": "dist/index.esm.js",
|
"module": "dist/index.esm.js",
|
||||||
@ -18,6 +18,7 @@
|
|||||||
"README.md"
|
"README.md"
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
"prepare": "npm run build",
|
||||||
"build": "rollup --config",
|
"build": "rollup --config",
|
||||||
"dev": "rollup --config --watch",
|
"dev": "rollup --config --watch",
|
||||||
"test": "echo \"Error: no test specified\" && exit 1",
|
"test": "echo \"Error: no test specified\" && exit 1",
|
||||||
@ -47,7 +48,7 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@rollup/plugin-typescript": "^8.2.1",
|
"@rollup/plugin-typescript": "^8.2.1",
|
||||||
"@sveltejs/kit": "^1.0.0-next.211",
|
"@sveltejs/kit": "^1.0.0-next.259",
|
||||||
"@types/jsonwebtoken": "^8.5.1",
|
"@types/jsonwebtoken": "^8.5.1",
|
||||||
"@typescript-eslint/eslint-plugin": "^4.23.0",
|
"@typescript-eslint/eslint-plugin": "^4.23.0",
|
||||||
"@typescript-eslint/parser": "^4.23.0",
|
"@typescript-eslint/parser": "^4.23.0",
|
||||||
|
51
src/auth.ts
51
src/auth.ts
@ -1,7 +1,6 @@
|
|||||||
import type { GetSession, RequestHandler } from "@sveltejs/kit";
|
import type { GetSession, RequestHandler } from "@sveltejs/kit";
|
||||||
import type { EndpointOutput } from "@sveltejs/kit/types/endpoint";
|
import type { EndpointOutput } from "@sveltejs/kit/types/endpoint";
|
||||||
import { RequestHeaders } from "@sveltejs/kit/types/helper";
|
import { RequestEvent } from "@sveltejs/kit/types/hooks";
|
||||||
import { ServerRequest } from "@sveltejs/kit/types/hooks";
|
|
||||||
import cookie from "cookie";
|
import cookie from "cookie";
|
||||||
import * as jsonwebtoken from "jsonwebtoken";
|
import * as jsonwebtoken from "jsonwebtoken";
|
||||||
import type { JWT, Session } from "./interfaces";
|
import type { JWT, Session } from "./interfaces";
|
||||||
@ -45,12 +44,12 @@ export class Auth {
|
|||||||
return "svelte_auth_secret";
|
return "svelte_auth_secret";
|
||||||
}
|
}
|
||||||
|
|
||||||
async getToken(headers: RequestHeaders) {
|
async getToken(headers: any) {
|
||||||
if (!headers.cookie) {
|
if (!headers.get("cookie")) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const cookies = cookie.parse(headers.cookie);
|
const cookies = cookie.parse(headers.get("cookie"));
|
||||||
|
|
||||||
if (!cookies.svelteauthjwt) {
|
if (!cookies.svelteauthjwt) {
|
||||||
return null;
|
return null;
|
||||||
@ -71,7 +70,7 @@ export class Auth {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getBaseUrl(host?: string) {
|
getBaseUrl(host?: string) {
|
||||||
const protocol = this.config?.protocol ?? "http";
|
const protocol = this.config?.protocol ?? "https";
|
||||||
host = this.config?.host ?? host;
|
host = this.config?.host ?? host;
|
||||||
return `${protocol}://${host}`;
|
return `${protocol}://${host}`;
|
||||||
}
|
}
|
||||||
@ -86,7 +85,7 @@ export class Auth {
|
|||||||
return new URL(pathname, this.getBaseUrl(host)).href;
|
return new URL(pathname, this.getBaseUrl(host)).href;
|
||||||
}
|
}
|
||||||
|
|
||||||
setToken(headers: RequestHeaders, newToken: JWT | any) {
|
setToken(headers: any, newToken: JWT | any) {
|
||||||
const originalToken = this.getToken(headers);
|
const originalToken = this.getToken(headers);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -113,12 +112,10 @@ export class Auth {
|
|||||||
return redirect;
|
return redirect;
|
||||||
}
|
}
|
||||||
|
|
||||||
async handleProviderCallback(
|
async handleProviderCallback(event: RequestEvent, provider: Provider): Promise<EndpointOutput> {
|
||||||
request: ServerRequest,
|
const { headers } = event.request;
|
||||||
provider: Provider,
|
const { url } = event;
|
||||||
): Promise<EndpointOutput> {
|
const [profile, redirectUrl] = await provider.callback(event, this);
|
||||||
const { headers, url } = request;
|
|
||||||
const [profile, redirectUrl] = await provider.callback(request, this);
|
|
||||||
|
|
||||||
let token = (await this.getToken(headers)) ?? { user: {} };
|
let token = (await this.getToken(headers)) ?? { user: {} };
|
||||||
if (this.config?.callbacks?.jwt) {
|
if (this.config?.callbacks?.jwt) {
|
||||||
@ -139,11 +136,12 @@ export class Auth {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async handleEndpoint(request: ServerRequest): Promise<EndpointOutput> {
|
async handleEndpoint(event: RequestEvent): Promise<EndpointOutput> {
|
||||||
const { headers, method, url } = request;
|
const { headers, method } = event.request;
|
||||||
|
const { url } = event;
|
||||||
|
|
||||||
if (url.pathname === this.getPath("signout")) {
|
if (url.pathname === this.getPath("signout")) {
|
||||||
const token = this.setToken(headers, {});
|
const token = this.setToken(event.request.headers, {});
|
||||||
const jwt = this.signToken(token);
|
const jwt = this.signToken(token);
|
||||||
|
|
||||||
if (method === "POST") {
|
if (method === "POST") {
|
||||||
@ -177,9 +175,9 @@ export class Auth {
|
|||||||
);
|
);
|
||||||
if (provider) {
|
if (provider) {
|
||||||
if (match.groups.method === "signin") {
|
if (match.groups.method === "signin") {
|
||||||
return await provider.signin(request, this);
|
return await provider.signin(event, this);
|
||||||
} else {
|
} else {
|
||||||
return await this.handleProviderCallback(request, provider);
|
return await this.handleProviderCallback(event, provider);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -190,13 +188,13 @@ export class Auth {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
get: RequestHandler = async (request) => {
|
get: RequestHandler = async (event: RequestEvent): Promise<any> => {
|
||||||
const { url } = request;
|
const { url } = event;
|
||||||
|
|
||||||
if (url.pathname === this.getPath("csrf")) {
|
if (url.pathname === this.getPath("csrf")) {
|
||||||
return { body: "1234" }; // TODO: Generate real token
|
return { body: "1234" }; // TODO: Generate real token
|
||||||
} else if (url.pathname === this.getPath("session")) {
|
} else if (url.pathname === this.getPath("session")) {
|
||||||
const session = await this.getSession(request);
|
const session = await this.getSession(event);
|
||||||
return {
|
return {
|
||||||
body: {
|
body: {
|
||||||
session,
|
session,
|
||||||
@ -204,15 +202,16 @@ export class Auth {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return await this.handleEndpoint(request);
|
return await this.handleEndpoint(event);
|
||||||
};
|
};
|
||||||
|
|
||||||
post: RequestHandler = async (request) => {
|
post: RequestHandler = async (event: RequestEvent) => {
|
||||||
return await this.handleEndpoint(request);
|
return await this.handleEndpoint(event);
|
||||||
};
|
};
|
||||||
|
|
||||||
getSession: GetSession = async ({ headers }) => {
|
getSession: GetSession = async (event: RequestEvent) => {
|
||||||
const token = await this.getToken(headers);
|
const { request } = event;
|
||||||
|
const token = await this.getToken(request.headers);
|
||||||
|
|
||||||
if (token) {
|
if (token) {
|
||||||
if (this.config?.callbacks?.session) {
|
if (this.config?.callbacks?.session) {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import type { EndpointOutput } from "@sveltejs/kit";
|
import type { EndpointOutput } from "@sveltejs/kit";
|
||||||
import { ServerRequest } from "@sveltejs/kit/types/hooks";
|
import { RequestEvent } from "@sveltejs/kit/types/hooks";
|
||||||
import type { Auth } from "../auth";
|
import type { Auth } from "../auth";
|
||||||
import type { CallbackResult } from "../types";
|
import type { CallbackResult } from "../types";
|
||||||
|
|
||||||
@ -28,12 +28,12 @@ export abstract class Provider<T extends ProviderConfig = ProviderConfig> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
abstract signin<Locals extends Record<string, any> = Record<string, any>, Body = unknown>(
|
abstract signin<Locals extends Record<string, any> = Record<string, any>, Body = unknown>(
|
||||||
request: ServerRequest<Locals, Body>,
|
event: RequestEvent,
|
||||||
svelteKitAuth: Auth,
|
svelteKitAuth: Auth,
|
||||||
): EndpointOutput | Promise<EndpointOutput>;
|
): EndpointOutput | Promise<EndpointOutput>;
|
||||||
|
|
||||||
abstract callback<Locals extends Record<string, any> = Record<string, any>, Body = unknown>(
|
abstract callback<Locals extends Record<string, any> = Record<string, any>, Body = unknown>(
|
||||||
request: ServerRequest<Locals, Body>,
|
event: RequestEvent,
|
||||||
svelteKitAuth: Auth,
|
svelteKitAuth: Auth,
|
||||||
): CallbackResult | Promise<CallbackResult>;
|
): CallbackResult | Promise<CallbackResult>;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import type { EndpointOutput } from "@sveltejs/kit/types/endpoint";
|
import type { EndpointOutput } from "@sveltejs/kit/types/endpoint";
|
||||||
import { ServerRequest } from "@sveltejs/kit/types/hooks";
|
import { RequestEvent } from "@sveltejs/kit/types/hooks";
|
||||||
import type { Auth } from "../auth";
|
import type { Auth } from "../auth";
|
||||||
import type { CallbackResult } from "../types";
|
import type { CallbackResult } from "../types";
|
||||||
import { Provider, ProviderConfig } from "./base";
|
import { Provider, ProviderConfig } from "./base";
|
||||||
@ -25,7 +25,7 @@ export abstract class OAuth2BaseProvider<
|
|||||||
T extends OAuth2BaseProviderConfig,
|
T extends OAuth2BaseProviderConfig,
|
||||||
> extends Provider<T> {
|
> extends Provider<T> {
|
||||||
abstract getAuthorizationUrl(
|
abstract getAuthorizationUrl(
|
||||||
request: ServerRequest,
|
event: RequestEvent,
|
||||||
auth: Auth,
|
auth: Auth,
|
||||||
state: string,
|
state: string,
|
||||||
nonce: string,
|
nonce: string,
|
||||||
@ -33,14 +33,15 @@ export abstract class OAuth2BaseProvider<
|
|||||||
abstract getTokens(code: string, redirectUri: string): TokensType | Promise<TokensType>;
|
abstract getTokens(code: string, redirectUri: string): TokensType | Promise<TokensType>;
|
||||||
abstract getUserProfile(tokens: any): ProfileType | Promise<ProfileType>;
|
abstract getUserProfile(tokens: any): ProfileType | Promise<ProfileType>;
|
||||||
|
|
||||||
async signin(request: ServerRequest, auth: Auth): Promise<EndpointOutput> {
|
async signin(event: RequestEvent, auth: Auth): Promise<EndpointOutput> {
|
||||||
const { method, url } = request;
|
const { method } = event.request;
|
||||||
|
const { url } = event;
|
||||||
const state = [
|
const state = [
|
||||||
`redirect=${url.searchParams.get("redirect") ?? this.getUri(auth, "/", url.host)}`,
|
`redirect=${url.searchParams.get("redirect") ?? this.getUri(auth, "/", url.host)}`,
|
||||||
].join(",");
|
].join(",");
|
||||||
const base64State = Buffer.from(state).toString("base64");
|
const base64State = Buffer.from(state).toString("base64");
|
||||||
const nonce = Math.round(Math.random() * 1000).toString(); // TODO: Generate random based on user values
|
const nonce = Math.round(Math.random() * 1000).toString(); // TODO: Generate random based on user values
|
||||||
const authUrl = await this.getAuthorizationUrl(request, auth, base64State, nonce);
|
const authUrl = await this.getAuthorizationUrl(event, auth, base64State, nonce);
|
||||||
|
|
||||||
if (method === "POST") {
|
if (method === "POST") {
|
||||||
return {
|
return {
|
||||||
@ -68,7 +69,8 @@ export abstract class OAuth2BaseProvider<
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async callback({ url }: ServerRequest, auth: Auth): Promise<CallbackResult> {
|
async callback(event: RequestEvent, auth: Auth): Promise<any> {
|
||||||
|
const { request, url } = event;
|
||||||
const code = url.searchParams.get("code");
|
const code = url.searchParams.get("code");
|
||||||
const redirect = this.getStateValue(url.searchParams, "redirect");
|
const redirect = this.getStateValue(url.searchParams, "redirect");
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { ServerRequest } from "@sveltejs/kit/types/hooks";
|
import { RequestEvent } from "@sveltejs/kit/types/hooks";
|
||||||
import type { Auth } from "../auth";
|
import type { Auth } from "../auth";
|
||||||
import { ucFirst } from "../helpers";
|
import { ucFirst } from "../helpers";
|
||||||
import { OAuth2BaseProvider, OAuth2BaseProviderConfig, OAuth2Tokens } from "./oauth2.base";
|
import { OAuth2BaseProvider, OAuth2BaseProviderConfig, OAuth2Tokens } from "./oauth2.base";
|
||||||
@ -37,7 +37,7 @@ export class OAuth2Provider<
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getAuthorizationUrl({ url }: ServerRequest, auth: Auth, state: string, nonce: string) {
|
getAuthorizationUrl({ url }: RequestEvent, auth: Auth, state: string, nonce: string) {
|
||||||
const data = {
|
const data = {
|
||||||
state,
|
state,
|
||||||
nonce,
|
nonce,
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import { OAuth2Provider, OAuth2ProviderConfig } from "./oauth2";
|
import { OAuth2Provider, OAuth2ProviderConfig } from "./oauth2";
|
||||||
import type { ProfileCallback } from "./oauth2.base";
|
|
||||||
|
|
||||||
export interface RedditProfile {
|
export interface RedditProfile {
|
||||||
is_employee: boolean;
|
is_employee: boolean;
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import { ServerRequest } from "@sveltejs/kit/types/hooks";
|
import { RequestEvent } from "@sveltejs/kit/types/hooks";
|
||||||
import type { Auth } from "../auth";
|
import type { Auth } from "../auth";
|
||||||
import type { CallbackResult } from "../types";
|
|
||||||
import { OAuth2BaseProvider, OAuth2BaseProviderConfig } from "./oauth2.base";
|
import { OAuth2BaseProvider, OAuth2BaseProviderConfig } from "./oauth2.base";
|
||||||
|
|
||||||
interface TwitterAuthProviderConfig extends OAuth2BaseProviderConfig {
|
interface TwitterAuthProviderConfig extends OAuth2BaseProviderConfig {
|
||||||
@ -38,7 +37,7 @@ export class TwitterAuthProvider extends OAuth2BaseProvider<any, any, TwitterAut
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async getAuthorizationUrl({ url }: ServerRequest, auth: Auth, state: string, nonce: string) {
|
async getAuthorizationUrl({ url }: RequestEvent, auth: Auth, state: string, nonce: string) {
|
||||||
const endpoint = "https://api.twitter.com/oauth/authorize";
|
const endpoint = "https://api.twitter.com/oauth/authorize";
|
||||||
|
|
||||||
const { oauthToken } = await this.getRequestToken(auth, url.host);
|
const { oauthToken } = await this.getRequestToken(auth, url.host);
|
||||||
@ -71,7 +70,8 @@ export class TwitterAuthProvider extends OAuth2BaseProvider<any, any, TwitterAut
|
|||||||
return await res.json();
|
return await res.json();
|
||||||
}
|
}
|
||||||
|
|
||||||
async callback({ url }: ServerRequest, auth: Auth): Promise<CallbackResult> {
|
async callback(event: RequestEvent, auth: Auth): Promise<any> {
|
||||||
|
const { url } = event;
|
||||||
const oauthToken = url.searchParams.get("oauth_token");
|
const oauthToken = url.searchParams.get("oauth_token");
|
||||||
const oauthVerifier = url.searchParams.get("oauth_verifier");
|
const oauthVerifier = url.searchParams.get("oauth_verifier");
|
||||||
const redirect = this.getStateValue(url.searchParams, "redirect");
|
const redirect = this.getStateValue(url.searchParams, "redirect");
|
||||||
|
Loading…
Reference in New Issue
Block a user