mirror of
https://github.com/Dan6erbond/sk-auth.git
synced 2024-11-20 19:07:20 +01:00
[ENHANCEMENT] Demo / Testing App, Updated Build Configuration (#8)
* 🎉 Scaffold example app with SvelteKit barebones skeleton * 💄 Add TWCSS and base styles with fonts Inter/Fira Mono * 🔧 Add `exports` and `types` to `package.json` and update `tsconfig.json` for Vite-compatible build output * ➕ Add local dependency to `svelte-kit-auth` as symlink * 🔧 Update example app env variables * ✨ Add basic auth config to example app * ♻️ Export all providers from `/providers` module * 🎨 Make `Auth` class default export of lib * 🚚 Rename `example-app` to `app` * ➕ Use `file:` instead of `link:` for local dependency to `svelte-kit-auth` * 🔧 Add `JWT_SECRET_KEY` to env and config * 🎨 Add `RedditOAuthProvider.profileHandler` for general use and stripping of payload * ✨ Export auth API routes from app * ⬆️ Update local deps * ✨ Add `host` and `basePath` to general config and improve recognition of routes * 🚨 Exclude `app` from TS build * 📌 Undo `file:` mapping dependency for usage with Vite TODO: Needs to be fixed for release. * 🎨 Enable TS `strict` mode and set target to `es2017` * 📌 Undo `file:` mapping dependency for usage with Vite * 🚨 Format and lint files * 🍱 Add logo * ✨ Add login and profile routes to example app for showcase and testing * 💄 Add PrismJS and create homepage with example * 🔨 Add `build:watch`
This commit is contained in:
parent
2b21911d22
commit
5d1802fea4
@ -12,6 +12,7 @@ module.exports = {
|
||||
"@typescript-eslint/explicit-module-boundary-types": "off",
|
||||
"@typescript-eslint/no-explicit-any": "off",
|
||||
"@typescript-eslint/no-unused-vars": ["off", { argsIgnorePattern: "^_" }],
|
||||
"@typescript-eslint/no-non-null-assertion": "off",
|
||||
},
|
||||
env: {
|
||||
browser: true,
|
||||
|
25
app/.eslintrc.cjs
Normal file
25
app/.eslintrc.cjs
Normal file
@ -0,0 +1,25 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
parser: "@typescript-eslint/parser",
|
||||
extends: [
|
||||
"eslint:recommended",
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"prettier",
|
||||
"../.eslintrc.cjs",
|
||||
],
|
||||
plugins: ["svelte3", "@typescript-eslint"],
|
||||
ignorePatterns: ["*.cjs"],
|
||||
overrides: [{ files: ["*.svelte"], processor: "svelte3/svelte3" }],
|
||||
settings: {
|
||||
"svelte3/typescript": () => require("typescript"),
|
||||
},
|
||||
parserOptions: {
|
||||
sourceType: "module",
|
||||
ecmaVersion: 2019,
|
||||
},
|
||||
env: {
|
||||
browser: true,
|
||||
es2017: true,
|
||||
node: true,
|
||||
},
|
||||
};
|
5
app/.gitignore
vendored
Normal file
5
app/.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
.DS_Store
|
||||
node_modules
|
||||
/.svelte-kit
|
||||
/build
|
||||
/functions
|
1
app/.npmrc
Normal file
1
app/.npmrc
Normal file
@ -0,0 +1 @@
|
||||
engine-strict=true
|
4
app/.prettierignore
Normal file
4
app/.prettierignore
Normal file
@ -0,0 +1,4 @@
|
||||
.svelte-kit/**
|
||||
static/**
|
||||
build/**
|
||||
node_modules/**
|
4
app/.prettierrc
Normal file
4
app/.prettierrc
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"trailingComma": "all",
|
||||
"printWidth": 100
|
||||
}
|
38
app/README.md
Normal file
38
app/README.md
Normal file
@ -0,0 +1,38 @@
|
||||
# create-svelte
|
||||
|
||||
Everything you need to build a Svelte project, powered by [`create-svelte`](https://github.com/sveltejs/kit/tree/master/packages/create-svelte);
|
||||
|
||||
## Creating a project
|
||||
|
||||
If you're seeing this, you've probably already done this step. Congrats!
|
||||
|
||||
```bash
|
||||
# create a new project in the current directory
|
||||
npm init svelte@next
|
||||
|
||||
# create a new project in my-app
|
||||
npm init svelte@next my-app
|
||||
```
|
||||
|
||||
> Note: the `@next` is temporary
|
||||
|
||||
## Developing
|
||||
|
||||
Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server:
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
|
||||
# or start the server and open the app in a new browser tab
|
||||
npm run dev -- --open
|
||||
```
|
||||
|
||||
## Building
|
||||
|
||||
Before creating a production version of your app, install an [adapter](https://kit.svelte.dev/docs#adapters) for your target environment. Then:
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
```
|
||||
|
||||
> You can preview the built app with `npm run preview`, regardless of whether you installed an adapter. This should _not_ be used to serve your app in production.
|
39
app/package.json
Normal file
39
app/package.json
Normal file
@ -0,0 +1,39 @@
|
||||
{
|
||||
"name": "~TODO~",
|
||||
"version": "0.0.1",
|
||||
"scripts": {
|
||||
"dev": "svelte-kit dev",
|
||||
"build": "svelte-kit build",
|
||||
"preview": "svelte-kit preview",
|
||||
"lint": "prettier --check --plugin-search-dir=. . && eslint --ignore-path .gitignore .",
|
||||
"format": "prettier --write --plugin-search-dir=. ."
|
||||
},
|
||||
"devDependencies": {
|
||||
"@sveltejs/kit": "next",
|
||||
"@types/prismjs": "^1.16.5",
|
||||
"@typescript-eslint/eslint-plugin": "^4.19.0",
|
||||
"@typescript-eslint/parser": "^4.19.0",
|
||||
"autoprefixer": "^10.2.5",
|
||||
"cssnano": "^5.0.1",
|
||||
"eslint": "^7.22.0",
|
||||
"eslint-config-prettier": "^8.1.0",
|
||||
"eslint-plugin-svelte3": "^3.2.0",
|
||||
"postcss": "^8.2.10",
|
||||
"postcss-load-config": "^3.0.1",
|
||||
"prettier": "~2.2.1",
|
||||
"prettier-plugin-svelte": "^2.2.0",
|
||||
"svelte": "^3.34.0",
|
||||
"svelte-preprocess": "^4.7.1",
|
||||
"tailwindcss": "^2.1.1",
|
||||
"tslib": "^2.0.0",
|
||||
"typescript": "^4.0.0"
|
||||
},
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"@fontsource/fira-mono": "^4.3.0",
|
||||
"@fontsource/inter": "^4.3.0",
|
||||
"clsx": "^1.1.1",
|
||||
"prismjs": "^1.23.0",
|
||||
"svelte-kit-auth": "link:../"
|
||||
}
|
||||
}
|
23
app/postcss.config.cjs
Normal file
23
app/postcss.config.cjs
Normal file
@ -0,0 +1,23 @@
|
||||
const tailwindcss = require("tailwindcss");
|
||||
const autoprefixer = require("autoprefixer");
|
||||
const cssnano = require("cssnano");
|
||||
|
||||
const mode = process.env.NODE_ENV;
|
||||
const dev = mode === "development";
|
||||
|
||||
module.exports = {
|
||||
plugins: [
|
||||
// Some plugins, like postcss-nested, need to run before Tailwind
|
||||
|
||||
tailwindcss,
|
||||
|
||||
// But others, like autoprefixer, need to run after
|
||||
|
||||
autoprefixer,
|
||||
|
||||
!dev &&
|
||||
cssnano({
|
||||
preset: "default",
|
||||
}),
|
||||
],
|
||||
};
|
12
app/src/app.html
Normal file
12
app/src/app.html
Normal file
@ -0,0 +1,12 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
%svelte.head%
|
||||
</head>
|
||||
<body>
|
||||
<div id="svelte">%svelte.body%</div>
|
||||
</body>
|
||||
</html>
|
74
app/src/app.postcss
Normal file
74
app/src/app.postcss
Normal file
@ -0,0 +1,74 @@
|
||||
@import "@fontsource/fira-mono";
|
||||
@import "@fontsource/inter";
|
||||
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
:root {
|
||||
font-family: "Inter", Arial, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu,
|
||||
Cantarell, "Open Sans", "Helvetica Neue", sans-serif;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 4px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
width: 6px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background-color: #c7c7c7;
|
||||
border-radius: 24px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background-color: #a1a1a1;
|
||||
border-radius: 24px;
|
||||
}
|
||||
|
||||
body::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
}
|
||||
|
||||
body::-webkit-scrollbar-track {
|
||||
width: 12px;
|
||||
}
|
||||
|
||||
body {
|
||||
min-height: 100vh;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
body::before {
|
||||
content: "";
|
||||
width: 80vw;
|
||||
height: 100vh;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 10vw;
|
||||
z-index: -1;
|
||||
opacity: 0.05;
|
||||
}
|
||||
|
||||
#svelte {
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2rem;
|
||||
margin-bottom: 0 0 1em 0;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
@media (min-width: 720px) {
|
||||
h1 {
|
||||
font-size: 2.4rem;
|
||||
}
|
||||
}
|
13
app/src/global.d.ts
vendored
Normal file
13
app/src/global.d.ts
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
/// <reference types="@sveltejs/kit" />
|
||||
|
||||
interface ImportMetaEnv {
|
||||
VITE_GOOGLE_OAUTH_CLIENT_ID: string;
|
||||
VITE_GOOGLE_OAUTH_CLIENT_SECRET: string;
|
||||
VITE_FACEBOOK_OAUTH_CLIENT_ID: string;
|
||||
VITE_FACEBOOK_OAUTH_CLIENT_SECRET: string;
|
||||
VITE_TWITTER_API_KEY: string;
|
||||
VITE_TWITTER_API_SECRET: string;
|
||||
VITE_REDDIT_API_KEY: string;
|
||||
VITE_REDDIT_API_SECRET: string;
|
||||
JWT_SECRET_KEY: string;
|
||||
}
|
15
app/src/hooks.ts
Normal file
15
app/src/hooks.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import type { Handle } from "@sveltejs/kit";
|
||||
import { appAuth } from "$lib/appAuth";
|
||||
|
||||
export const handle: Handle = async ({ request, render }) => {
|
||||
// TODO https://github.com/sveltejs/kit/issues/1046
|
||||
if (request.query.has("_method")) {
|
||||
request.method = request.query.get("_method").toUpperCase();
|
||||
}
|
||||
|
||||
const response = await render(request);
|
||||
|
||||
return response;
|
||||
};
|
||||
|
||||
export const { getSession } = appAuth;
|
58
app/src/lib/appAuth.ts
Normal file
58
app/src/lib/appAuth.ts
Normal file
@ -0,0 +1,58 @@
|
||||
import SvelteKitAuth from "svelte-kit-auth";
|
||||
import {
|
||||
FacebookAuthProvider,
|
||||
GoogleOAuthProvider,
|
||||
RedditOAuthProvider,
|
||||
TwitterAuthProvider,
|
||||
} from "svelte-kit-auth/providers";
|
||||
|
||||
export const appAuth = new SvelteKitAuth({
|
||||
providers: [
|
||||
new GoogleOAuthProvider({
|
||||
clientId: import.meta.env.VITE_GOOGLE_OAUTH_CLIENT_ID,
|
||||
clientSecret: import.meta.env.VITE_GOOGLE_OAUTH_CLIENT_SECRET,
|
||||
profile(profile) {
|
||||
return { ...profile, provider: "google" };
|
||||
},
|
||||
}),
|
||||
new FacebookAuthProvider({
|
||||
clientId: import.meta.env.VITE_FACEBOOK_OAUTH_CLIENT_ID,
|
||||
clientSecret: import.meta.env.VITE_FACEBOOK_OAUTH_CLIENT_SECRET,
|
||||
profile(profile) {
|
||||
return { ...profile, provider: "facebook" };
|
||||
},
|
||||
}),
|
||||
new TwitterAuthProvider({
|
||||
apiKey: import.meta.env.VITE_TWITTER_API_KEY,
|
||||
apiSecret: import.meta.env.VITE_TWITTER_API_SECRET,
|
||||
profile(profile) {
|
||||
return { ...profile, provider: "twitter" };
|
||||
},
|
||||
}),
|
||||
new RedditOAuthProvider({
|
||||
apiKey: import.meta.env.VITE_REDDIT_API_KEY,
|
||||
apiSecret: import.meta.env.VITE_REDDIT_API_SECRET,
|
||||
profile(profile) {
|
||||
profile = RedditOAuthProvider.profileHandler(profile);
|
||||
return { ...profile, provider: "reddit" };
|
||||
},
|
||||
}),
|
||||
],
|
||||
callbacks: {
|
||||
jwt(token, profile) {
|
||||
if (profile?.provider) {
|
||||
const { provider, ...account } = profile;
|
||||
token = {
|
||||
...token,
|
||||
user: {
|
||||
...token.user,
|
||||
connections: { [provider]: account },
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return token;
|
||||
},
|
||||
},
|
||||
jwtSecret: import.meta.env.JWT_SECRET_KEY,
|
||||
});
|
198
app/src/routes/__layout.svelte
Normal file
198
app/src/routes/__layout.svelte
Normal file
@ -0,0 +1,198 @@
|
||||
<script lang="ts">
|
||||
import "../app.postcss";
|
||||
import { page, session } from "$app/stores";
|
||||
import { signOut } from "svelte-kit-auth/client";
|
||||
import clsx from "clsx";
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>SvelteKitAuth</title>
|
||||
</svelte:head>
|
||||
|
||||
<header
|
||||
class={clsx(
|
||||
"flex",
|
||||
"justify-between",
|
||||
"fixed",
|
||||
"top-0",
|
||||
"left-0",
|
||||
"right-0",
|
||||
"w-full",
|
||||
"p-4",
|
||||
"items-center",
|
||||
"shadow-md",
|
||||
"bg-white",
|
||||
)}
|
||||
>
|
||||
<a href="/" class={clsx("flex", "items-center", "space-x-2")}>
|
||||
<img src="/logo.svg" class={clsx("h-8", "w-8")} alt="SvelteKitAuth Logo" />
|
||||
<span class={clsx("text-2xl", "text-orange-500")}>SvelteKitAuth</span>
|
||||
</a>
|
||||
<div class={clsx("flex", "items-center", "space-x-2")}>
|
||||
{#if $session?.user}
|
||||
<a
|
||||
href="/profile"
|
||||
class={clsx(
|
||||
"flex",
|
||||
"items-center",
|
||||
"justify-center",
|
||||
"hover:no-underline",
|
||||
"hover:bg-orange-50",
|
||||
"transition-colors",
|
||||
"p-2",
|
||||
"rounded-full",
|
||||
)}
|
||||
>
|
||||
{#if $page.path === "/profile"}
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class={clsx("h-6", "w-6")}
|
||||
viewBox="0 0 20 20"
|
||||
fill="currentColor"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M10 9a3 3 0 100-6 3 3 0 000 6zm-7 9a7 7 0 1114 0H3z"
|
||||
clip-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
{:else}
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class={clsx("h-6", "w-6")}
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"
|
||||
/>
|
||||
</svg>
|
||||
{/if}
|
||||
</a>
|
||||
<button
|
||||
class={clsx(
|
||||
"flex",
|
||||
"items-center",
|
||||
"justify-center",
|
||||
"hover:no-underline",
|
||||
"hover:bg-orange-50",
|
||||
"transition-colors",
|
||||
"p-2",
|
||||
"rounded-full",
|
||||
)}
|
||||
on:click={signOut}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class={clsx("h-6", "w-6")}
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
{:else}
|
||||
<a
|
||||
href="/login"
|
||||
class={clsx(
|
||||
"flex",
|
||||
"items-center",
|
||||
"justify-center",
|
||||
"hover:no-underline",
|
||||
"hover:bg-orange-50",
|
||||
"transition-colors",
|
||||
"p-2",
|
||||
"rounded-full",
|
||||
)}
|
||||
class:text-orange-500={$page.path === "/login"}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class={clsx("h-6", "w-6")}
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M11 16l-4-4m0 0l4-4m-4 4h14m-5 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h7a3 3 0 013 3v1"
|
||||
/>
|
||||
</svg>
|
||||
</a>
|
||||
{/if}
|
||||
<a
|
||||
href="https://github.com/Dan6erbond/SvelteKitAuth"
|
||||
target="_blank"
|
||||
class={clsx(
|
||||
"flex",
|
||||
"items-center",
|
||||
"justify-center",
|
||||
"hover:no-underline",
|
||||
"hover:bg-orange-50",
|
||||
"transition-colors",
|
||||
"p-2",
|
||||
"rounded-full",
|
||||
)}
|
||||
>
|
||||
<svg class={clsx("h-6", "w-6")} viewBox="0 0 128 128">
|
||||
<g fill="currentColor">
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M64 5.103c-33.347 0-60.388 27.035-60.388 60.388 0 26.682 17.303 49.317 41.297 57.303 3.017.56 4.125-1.31 4.125-2.905 0-1.44-.056-6.197-.082-11.243-16.8 3.653-20.345-7.125-20.345-7.125-2.747-6.98-6.705-8.836-6.705-8.836-5.48-3.748.413-3.67.413-3.67 6.063.425 9.257 6.223 9.257 6.223 5.386 9.23 14.127 6.562 17.573 5.02.542-3.903 2.107-6.568 3.834-8.076-13.413-1.525-27.514-6.704-27.514-29.843 0-6.593 2.36-11.98 6.223-16.21-.628-1.52-2.695-7.662.584-15.98 0 0 5.07-1.623 16.61 6.19C53.7 35 58.867 34.327 64 34.304c5.13.023 10.3.694 15.127 2.033 11.526-7.813 16.59-6.19 16.59-6.19 3.287 8.317 1.22 14.46.593 15.98 3.872 4.23 6.215 9.617 6.215 16.21 0 23.194-14.127 28.3-27.574 29.796 2.167 1.874 4.097 5.55 4.097 11.183 0 8.08-.07 14.583-.07 16.572 0 1.607 1.088 3.49 4.148 2.897 23.98-7.994 41.263-30.622 41.263-57.294C124.388 32.14 97.35 5.104 64 5.104z"
|
||||
/>
|
||||
<path
|
||||
d="M26.484 91.806c-.133.3-.605.39-1.035.185-.44-.196-.685-.605-.543-.906.13-.31.603-.395 1.04-.188.44.197.69.61.537.91zm-.743-.55M28.93 94.535c-.287.267-.85.143-1.232-.28-.396-.42-.47-.983-.177-1.254.298-.266.844-.14 1.24.28.394.426.472.984.17 1.255zm-.575-.618M31.312 98.012c-.37.258-.976.017-1.35-.52-.37-.538-.37-1.183.01-1.44.373-.258.97-.025 1.35.507.368.545.368 1.19-.01 1.452zm0 0M34.573 101.373c-.33.365-1.036.267-1.552-.23-.527-.487-.674-1.18-.343-1.544.336-.366 1.045-.264 1.564.23.527.486.686 1.18.333 1.543zm0 0M39.073 103.324c-.147.473-.825.688-1.51.486-.683-.207-1.13-.76-.99-1.238.14-.477.823-.7 1.512-.485.683.206 1.13.756.988 1.237zm0 0M44.016 103.685c.017.498-.563.91-1.28.92-.723.017-1.308-.387-1.315-.877 0-.503.568-.91 1.29-.924.717-.013 1.306.387 1.306.88zm0 0M48.614 102.903c.086.485-.413.984-1.126 1.117-.7.13-1.35-.172-1.44-.653-.086-.498.422-.997 1.122-1.126.714-.123 1.354.17 1.444.663zm0 0"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main
|
||||
class={clsx(
|
||||
"flex",
|
||||
"flex-1",
|
||||
"flex-col",
|
||||
"p-4",
|
||||
"w-full",
|
||||
"max-w-5xl",
|
||||
"my-0",
|
||||
"mx-auto",
|
||||
"box-border",
|
||||
"mt-16",
|
||||
"md:mt-20",
|
||||
"md:px-6",
|
||||
)}
|
||||
>
|
||||
<slot />
|
||||
</main>
|
||||
|
||||
<footer
|
||||
class={clsx(
|
||||
"p-4",
|
||||
"md:p-8",
|
||||
"shadow-2xl",
|
||||
"w-full",
|
||||
"flex",
|
||||
"items-center",
|
||||
"flex-col",
|
||||
"bg-orange-600",
|
||||
"text-white"
|
||||
)}
|
||||
>
|
||||
<span>© RaviAnand M, 2021</span>
|
||||
</footer>
|
3
app/src/routes/api/auth/[...auth].ts
Normal file
3
app/src/routes/api/auth/[...auth].ts
Normal file
@ -0,0 +1,3 @@
|
||||
import { appAuth } from "$lib/appAuth";
|
||||
|
||||
export const { get, post } = appAuth;
|
100
app/src/routes/index.svelte
Normal file
100
app/src/routes/index.svelte
Normal file
@ -0,0 +1,100 @@
|
||||
<script context="module">
|
||||
import { dev } from "$app/env";
|
||||
import clsx from "clsx";
|
||||
import Prism from "prismjs";
|
||||
import { onMount } from "svelte";
|
||||
import "prismjs/plugins/toolbar/prism-toolbar.css";
|
||||
import "prismjs/themes/prism-tomorrow.css";
|
||||
import "clipboard";
|
||||
|
||||
export const hydrate = dev;
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
const code = `export const appAuth = new SvelteKitAuth({
|
||||
providers: [
|
||||
new GoogleOAuthProvider({
|
||||
clientId: import.meta.env.VITE_GOOGLE_OAUTH_CLIENT_ID,
|
||||
clientSecret: import.meta.env.VITE_GOOGLE_OAUTH_CLIENT_SECRET,
|
||||
profile(profile) {
|
||||
return { ...profile, provider: "google" };
|
||||
},
|
||||
}),
|
||||
],
|
||||
callbacks: {
|
||||
jwt(token, profile) {
|
||||
if (profile?.provider) {
|
||||
const { provider, ...account } = profile;
|
||||
token = {
|
||||
...token,
|
||||
user: {
|
||||
...token.user,
|
||||
connections: { [provider]: account },
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return token;
|
||||
},
|
||||
},
|
||||
jwtSecret: import.meta.env.JWT_SECRET_KEY,
|
||||
});`;
|
||||
|
||||
onMount(async () => {
|
||||
await import("prismjs/components/prism-clike");
|
||||
await import("prismjs/components/prism-javascript");
|
||||
await import("prismjs/components/prism-typescript");
|
||||
Prism.highlightAll();
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class={clsx("flex", "justify-center", "items-center", "p-6", "md:p-12", "mb-6", "md:mb-12")}>
|
||||
<img src="/logo.svg" class="h-36 md:h-48" alt="" />
|
||||
<div class={clsx("space-y-6")}>
|
||||
<h1 class={clsx("text-center", "text-5xl", "md:text-6xl", "lg:text-7xl", "font-bold")}>
|
||||
SvelteKitAuth
|
||||
</h1>
|
||||
<p class={clsx("text-center", "text-xl")}>
|
||||
Authentication built from the ground up for SvelteKit.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class={clsx("flex", "justify-between", "mb-6", "md:mb-12")}>
|
||||
<div class={clsx("flex-1", "p-4")}>
|
||||
<p class={clsx("font-bold", "text-xl", "text-center", "mb-6")}>Easy</p>
|
||||
<ul class={clsx("text-center", "space-y-2")}>
|
||||
<ol>Supports OAuth and credential login out of the box</ol>
|
||||
<ol>Works with SvelteKit and Vite</ol>
|
||||
<ol>Easily extensible with alternative providers</ol>
|
||||
<ol>Build dynamic login screens and fetch sessions using client helpers within minutes</ol>
|
||||
</ul>
|
||||
</div>
|
||||
<div class={clsx("flex-1", "p-4")}>
|
||||
<p class={clsx("font-bold", "text-xl", "text-center", "mb-6")}>Flexible</p>
|
||||
<ul class={clsx("text-center", "space-y-2")}>
|
||||
<ol>Add new providers in minutes with class-based architecture</ol>
|
||||
<ol>Connect to a custom backend with callbacks</ol>
|
||||
<ol>Transform the session and JWT as you like using hooks</ol>
|
||||
<ol>Fetch additional account information after the initial sign-on</ol>
|
||||
</ul>
|
||||
</div>
|
||||
<div class={clsx("flex-1", "p-4")}>
|
||||
<p class={clsx("font-bold", "text-xl", "text-center", "mb-6")}>Secure</p>
|
||||
<ul class={clsx("text-center", "space-y-2")}>
|
||||
<ol>Uses signed JWT and HTTP-only cookies for secure sessions</ol>
|
||||
<ol>Allows you to manually sign and secure JWTs using an object-oriented approach</ol>
|
||||
<ol>Replay and state validation with supporting providers</ol>
|
||||
<ol>CSRF token validation</ol>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p class={clsx("text-2xl", "text-center", "mb-2")}>Dead-Simple Authentication in Minutes!</p>
|
||||
<p class={clsx("text-lg", "text-center", "mb-6")}>Test it out for yourself <a href="/login" class={clsx("hover:text-orange-500", "transition-colors")}>here</a>.</p>
|
||||
|
||||
<pre class="language-ts">
|
||||
<code class="language-ts">
|
||||
{code}
|
||||
</code>
|
||||
</pre>
|
194
app/src/routes/login.svelte
Normal file
194
app/src/routes/login.svelte
Normal file
@ -0,0 +1,194 @@
|
||||
<script context="module">
|
||||
import { dev } from "$app/env";
|
||||
import clsx from "clsx";
|
||||
|
||||
export const hydrate = dev;
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>Login | SvelteKitAuth</title>
|
||||
</svelte:head>
|
||||
|
||||
<h1>Welcome to SvelteKitAuth</h1>
|
||||
|
||||
<section
|
||||
class={clsx(
|
||||
"flex",
|
||||
"items-center",
|
||||
"flex-col",
|
||||
"mt-4",
|
||||
"sm:mt-8",
|
||||
"md:mt-16",
|
||||
"shadow",
|
||||
"py-8",
|
||||
"sm:py-16",
|
||||
"md:py-32",
|
||||
"px-8",
|
||||
"sm:px-16 md:px-32 self-center",
|
||||
)}
|
||||
>
|
||||
<h1 class={clsx("text-xl", "md:text-2xl")}>Login</h1>
|
||||
<hr class={clsx("my-3", "border-gray-400", "w-full")} />
|
||||
<p class={clsx("text-sm", "md:text-base", "text-gray-400", "max-w-lg")}>
|
||||
Login with one of the configured social providers to test the social login.
|
||||
</p>
|
||||
|
||||
<br />
|
||||
|
||||
<div class={clsx("flex", "flex-col", "justify-items-stretch", "space-y-4")}>
|
||||
<a
|
||||
href="/api/auth/signin/google"
|
||||
class={clsx(
|
||||
"text-sm",
|
||||
"md:text-base",
|
||||
"inline-flex",
|
||||
"space-x-4",
|
||||
"py-2",
|
||||
"px-4",
|
||||
"border-gray-400",
|
||||
"rounded",
|
||||
"hover:no-underline",
|
||||
"border",
|
||||
"hover:bg-gray-100",
|
||||
"transition-colors",
|
||||
"items-center",
|
||||
)}
|
||||
>
|
||||
<svg viewBox="0 0 128 128" class={clsx("h-4", "w-4", "md:h-6", "md:w-6")}>
|
||||
<g id="original">
|
||||
<path
|
||||
fill="#fff"
|
||||
d="M44.59,4.21a63.28,63.28,0,0,0,4.33,120.9,67.6,67.6,0,0,0,32.36.35A57.13,57.13,0,0,0,107.18,112a57.44,57.44,0,0,0,16-26.26,74.33,74.33,0,0,0,1.61-33.58H65.27c0,8.23,0,16.46,0,24.69H99.74A29.72,29.72,0,0,1,87.08,96.37a36.16,36.16,0,0,1-13.93,5.5,41.29,41.29,0,0,1-15.1,0A37.16,37.16,0,0,1,44,95.74a39.3,39.3,0,0,1-14.5-19.42,38.31,38.31,0,0,1,0-24.63,39.25,39.25,0,0,1,9.18-14.91A37.17,37.17,0,0,1,76.13,27a34.28,34.28,0,0,1,13.64,8q5.83-5.8,11.64-11.63c2-2.09,4.18-4.08,6.15-6.22A61.22,61.22,0,0,0,87.2,4.59,64,64,0,0,0,44.59,4.21Z"
|
||||
/>
|
||||
<path
|
||||
fill="#e33629"
|
||||
d="M44.59,4.21a64,64,0,0,1,42.61.37A61.22,61.22,0,0,1,107.55,17.2c-2,2.14-4.11,4.14-6.15,6.22Q95.58,29.23,89.77,35a34.28,34.28,0,0,0-13.64-8,37.17,37.17,0,0,0-37.46,9.74,39.25,39.25,0,0,0-9.18,14.91L8.76,35.6A63.53,63.53,0,0,1,44.59,4.21Z"
|
||||
/>
|
||||
<path
|
||||
fill="#f8bd00"
|
||||
d="M3.26,51.5a62.93,62.93,0,0,1,5.5-15.9L29.49,51.69a38.31,38.31,0,0,0,0,24.63q-10.36,8-20.73,16.08A63.33,63.33,0,0,1,3.26,51.5Z"
|
||||
/>
|
||||
<path
|
||||
fill="#587dbd"
|
||||
d="M65.27,52.15h59.52a74.33,74.33,0,0,1-1.61,33.58,57.44,57.44,0,0,1-16,26.26c-6.69-5.22-13.41-10.4-20.1-15.62A29.72,29.72,0,0,0,99.74,76.83H65.27C65.26,68.61,65.27,60.38,65.27,52.15Z"
|
||||
/>
|
||||
<path
|
||||
fill="#319f43"
|
||||
d="M8.75,92.4q10.37-8,20.73-16.08A39.3,39.3,0,0,0,44,95.74a37.16,37.16,0,0,0,14.08,6.08,41.29,41.29,0,0,0,15.1,0,36.16,36.16,0,0,0,13.93-5.5c6.69,5.22,13.41,10.4,20.1,15.62a57.13,57.13,0,0,1-25.9,13.47,67.6,67.6,0,0,1-32.36-.35,63,63,0,0,1-23-11.59A63.73,63.73,0,0,1,8.75,92.4Z"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
<span>Sign in with Google</span>
|
||||
</a>
|
||||
|
||||
<a
|
||||
href="/api/auth/signin/facebook"
|
||||
class={clsx(
|
||||
"text-sm",
|
||||
"md:text-base",
|
||||
"inline-flex",
|
||||
"space-x-4",
|
||||
"py-2",
|
||||
"px-4",
|
||||
"border-gray-400",
|
||||
"rounded",
|
||||
"hover:no-underline",
|
||||
"border",
|
||||
"hover:bg-gray-100",
|
||||
"transition-colors",
|
||||
"items-center",
|
||||
)}
|
||||
>
|
||||
<svg viewBox="0 0 128 128" class={clsx("h-4", "w-4", "md:h-6", "md:w-6")}>
|
||||
<g id="original">
|
||||
<rect
|
||||
id="Blue"
|
||||
fill="#3d5a98"
|
||||
x="4.83"
|
||||
y="4.83"
|
||||
width="118.35"
|
||||
height="118.35"
|
||||
rx="6.53"
|
||||
ry="6.53"
|
||||
/>
|
||||
<path
|
||||
id="f"
|
||||
fill="#fff"
|
||||
d="M86.48,123.17V77.34h15.38l2.3-17.86H86.48V48.08c0-5.17,1.44-8.7,8.85-8.7h9.46v-16A126.56,126.56,0,0,0,91,22.7C77.38,22.7,68,31,68,46.31V59.48H52.62V77.34H68v45.83Z"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
<span>Sign in with Facebook</span>
|
||||
</a>
|
||||
|
||||
<a
|
||||
href="/api/auth/signin/reddit"
|
||||
class={clsx(
|
||||
"text-sm",
|
||||
"md:text-base",
|
||||
"inline-flex",
|
||||
"space-x-4",
|
||||
"py-2",
|
||||
"px-4",
|
||||
"border-gray-400",
|
||||
"rounded",
|
||||
"hover:no-underline",
|
||||
"border",
|
||||
"hover:bg-gray-100",
|
||||
"transition-colors",
|
||||
"items-center",
|
||||
)}
|
||||
>
|
||||
<svg viewBox="0 0 24 24" class={clsx("h-4", "w-4", "md:h-6", "md:w-6")}>
|
||||
<path
|
||||
fill="#FF4300"
|
||||
d="M14.5 15.41C14.58 15.5 14.58 15.69 14.5 15.8C13.77 16.5 12.41 16.56 12 16.56C11.61 16.56 10.25 16.5 9.54 15.8C9.44 15.69 9.44 15.5 9.54 15.41C9.65 15.31 9.82 15.31 9.92 15.41C10.38 15.87 11.33 16 12 16C12.69 16 13.66 15.87 14.1 15.41C14.21 15.31 14.38 15.31 14.5 15.41M10.75 13.04C10.75 12.47 10.28 12 9.71 12C9.14 12 8.67 12.47 8.67 13.04C8.67 13.61 9.14 14.09 9.71 14.08C10.28 14.08 10.75 13.61 10.75 13.04M14.29 12C13.72 12 13.25 12.5 13.25 13.05S13.72 14.09 14.29 14.09C14.86 14.09 15.33 13.61 15.33 13.05C15.33 12.5 14.86 12 14.29 12M22 12C22 17.5 17.5 22 12 22S2 17.5 2 12C2 6.5 6.5 2 12 2S22 6.5 22 12M18.67 12C18.67 11.19 18 10.54 17.22 10.54C16.82 10.54 16.46 10.7 16.2 10.95C15.2 10.23 13.83 9.77 12.3 9.71L12.97 6.58L15.14 7.05C15.16 7.6 15.62 8.04 16.18 8.04C16.75 8.04 17.22 7.57 17.22 7C17.22 6.43 16.75 5.96 16.18 5.96C15.77 5.96 15.41 6.2 15.25 6.55L12.82 6.03C12.75 6 12.68 6.03 12.63 6.07C12.57 6.11 12.54 6.17 12.53 6.24L11.79 9.72C10.24 9.77 8.84 10.23 7.82 10.96C7.56 10.71 7.2 10.56 6.81 10.56C6 10.56 5.35 11.21 5.35 12C5.35 12.61 5.71 13.11 6.21 13.34C6.19 13.5 6.18 13.62 6.18 13.78C6.18 16 8.79 17.85 12 17.85C15.23 17.85 17.85 16.03 17.85 13.78C17.85 13.64 17.84 13.5 17.81 13.34C18.31 13.11 18.67 12.6 18.67 12Z"
|
||||
/>
|
||||
</svg>
|
||||
<span>Sign in with Reddit</span>
|
||||
</a>
|
||||
|
||||
<p class={clsx("text-gray-600", "text-center", "border-gray-400", "border-b", "pb-2")}>
|
||||
Coming soon.
|
||||
</p>
|
||||
|
||||
<div
|
||||
class={clsx(
|
||||
"text-sm",
|
||||
"md:text-base",
|
||||
"inline-flex",
|
||||
"space-x-4",
|
||||
"py-2",
|
||||
"px-4",
|
||||
"border-gray-400",
|
||||
"rounded",
|
||||
"hover:no-underline",
|
||||
"border",
|
||||
"hover:bg-gray-100",
|
||||
"transition-colors",
|
||||
"items-center",
|
||||
"text-gray-500",
|
||||
"cursor-not-allowed",
|
||||
)}
|
||||
>
|
||||
<svg
|
||||
viewBox="0 0 128 128"
|
||||
class={clsx("h-4", "w-4", "md:h-6", "md:w-6", "filter", "grayscale", "opacity-60")}
|
||||
>
|
||||
<g id="surface1">
|
||||
<path
|
||||
style=" stroke:none;fill-rule:nonzero;fill:rgb(11.372549%,63.137255%,94.901961%);fill-opacity:1;"
|
||||
d="M 40.253906 127.636719 C 88.558594 127.636719 114.972656 78.679688 114.972656 36.234375 C 114.972656 34.84375 114.972656 33.457031 114.898438 32.078125 C 120.039062 27.53125 124.476562 21.898438 128 15.445312 C 123.210938 18.046875 118.128906 19.75 112.921875 20.507812 C 118.402344 16.488281 122.503906 10.171875 124.460938 2.734375 C 119.304688 6.476562 113.664062 9.113281 107.78125 10.535156 C 99.644531 -0.0507812 86.710938 -2.644531 76.234375 4.214844 C 65.761719 11.074219 60.351562 25.675781 63.035156 39.832031 C 41.921875 38.539062 22.246094 26.335938 8.914062 6.269531 C 1.933594 20.941406 5.488281 39.722656 17.023438 49.160156 C 12.875 48.988281 8.816406 47.605469 5.191406 45.128906 C 5.191406 45.265625 5.191406 45.402344 5.191406 45.539062 C 5.191406 60.8125 13.976562 73.976562 26.210938 77.03125 C 22.34375 78.320312 18.285156 78.503906 14.347656 77.574219 C 17.785156 90.667969 27.644531 99.644531 38.882812 99.902344 C 29.578125 108.820312 18.089844 113.652344 6.265625 113.621094 C 4.171875 113.621094 2.078125 113.472656 0 113.175781 C 12.007812 122.609375 25.980469 127.617188 40.253906 127.597656 "
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
<span>Sign in with Twitter</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<br />
|
||||
|
||||
<p class={clsx("text-sm", "md:text-base", "text-orange-400", "max-w-lg")}>
|
||||
We will never share your identity with anyone else.
|
||||
</p>
|
||||
</section>
|
215
app/src/routes/profile.svelte
Normal file
215
app/src/routes/profile.svelte
Normal file
@ -0,0 +1,215 @@
|
||||
<script context="module">
|
||||
import { dev } from "$app/env";
|
||||
import { session } from "$app/stores";
|
||||
import clsx from "clsx";
|
||||
|
||||
export const hydrate = dev;
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>Profile | SvelteKitAuth</title>
|
||||
</svelte:head>
|
||||
|
||||
<h1 class={clsx("mb-4")}>Your Profile</h1>
|
||||
|
||||
<div class={clsx("flex", "flex-col")}>
|
||||
<p class={clsx("text-lg", "mb-2")}>Connections</p>
|
||||
|
||||
<div
|
||||
class={clsx(
|
||||
"grid",
|
||||
"grid-cols-2",
|
||||
"sm:grid-cols-3",
|
||||
"md:grid-cols-4",
|
||||
"lg:grid-cols-6",
|
||||
"gap-4",
|
||||
"mb-6",
|
||||
)}
|
||||
>
|
||||
<div class={clsx("flex", "items-center", "space-x-4")}>
|
||||
<svg viewBox="0 0 128 128" class={clsx("h-4", "w-4", "md:h-6", "md:w-6")}>
|
||||
<g id="original">
|
||||
<path
|
||||
fill="#fff"
|
||||
d="M44.59,4.21a63.28,63.28,0,0,0,4.33,120.9,67.6,67.6,0,0,0,32.36.35A57.13,57.13,0,0,0,107.18,112a57.44,57.44,0,0,0,16-26.26,74.33,74.33,0,0,0,1.61-33.58H65.27c0,8.23,0,16.46,0,24.69H99.74A29.72,29.72,0,0,1,87.08,96.37a36.16,36.16,0,0,1-13.93,5.5,41.29,41.29,0,0,1-15.1,0A37.16,37.16,0,0,1,44,95.74a39.3,39.3,0,0,1-14.5-19.42,38.31,38.31,0,0,1,0-24.63,39.25,39.25,0,0,1,9.18-14.91A37.17,37.17,0,0,1,76.13,27a34.28,34.28,0,0,1,13.64,8q5.83-5.8,11.64-11.63c2-2.09,4.18-4.08,6.15-6.22A61.22,61.22,0,0,0,87.2,4.59,64,64,0,0,0,44.59,4.21Z"
|
||||
/>
|
||||
<path
|
||||
fill="#e33629"
|
||||
d="M44.59,4.21a64,64,0,0,1,42.61.37A61.22,61.22,0,0,1,107.55,17.2c-2,2.14-4.11,4.14-6.15,6.22Q95.58,29.23,89.77,35a34.28,34.28,0,0,0-13.64-8,37.17,37.17,0,0,0-37.46,9.74,39.25,39.25,0,0,0-9.18,14.91L8.76,35.6A63.53,63.53,0,0,1,44.59,4.21Z"
|
||||
/>
|
||||
<path
|
||||
fill="#f8bd00"
|
||||
d="M3.26,51.5a62.93,62.93,0,0,1,5.5-15.9L29.49,51.69a38.31,38.31,0,0,0,0,24.63q-10.36,8-20.73,16.08A63.33,63.33,0,0,1,3.26,51.5Z"
|
||||
/>
|
||||
<path
|
||||
fill="#587dbd"
|
||||
d="M65.27,52.15h59.52a74.33,74.33,0,0,1-1.61,33.58,57.44,57.44,0,0,1-16,26.26c-6.69-5.22-13.41-10.4-20.1-15.62A29.72,29.72,0,0,0,99.74,76.83H65.27C65.26,68.61,65.27,60.38,65.27,52.15Z"
|
||||
/>
|
||||
<path
|
||||
fill="#319f43"
|
||||
d="M8.75,92.4q10.37-8,20.73-16.08A39.3,39.3,0,0,0,44,95.74a37.16,37.16,0,0,0,14.08,6.08,41.29,41.29,0,0,0,15.1,0,36.16,36.16,0,0,0,13.93-5.5c6.69,5.22,13.41,10.4,20.1,15.62a57.13,57.13,0,0,1-25.9,13.47,67.6,67.6,0,0,1-32.36-.35,63,63,0,0,1-23-11.59A63.73,63.73,0,0,1,8.75,92.4Z"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
<div class={clsx("flex", "flex-col", "items-start", "space-y-1")}>
|
||||
{#if $session.user.connections.google}
|
||||
<p class={clsx("font-bold")}>Signed in as:</p>
|
||||
{$session.user.connections.google.name}
|
||||
{:else}
|
||||
<p class={clsx("font-bold")}>Not signed in</p>
|
||||
<a
|
||||
href="/api/auth/signin/google"
|
||||
class={clsx(
|
||||
"text-xs",
|
||||
"md:text-sm",
|
||||
"py-1",
|
||||
"px-2",
|
||||
"border-gray-400",
|
||||
"rounded",
|
||||
"hover:no-underline",
|
||||
"border",
|
||||
"hover:bg-gray-100",
|
||||
"transition-colors",
|
||||
"items-center",
|
||||
)}
|
||||
>
|
||||
Connect
|
||||
</a>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class={clsx("flex", "items-center", "space-x-4")}>
|
||||
<svg viewBox="0 0 128 128" class={clsx("h-4", "w-4", "md:h-6", "md:w-6")}>
|
||||
<g id="original">
|
||||
<rect
|
||||
id="Blue"
|
||||
fill="#3d5a98"
|
||||
x="4.83"
|
||||
y="4.83"
|
||||
width="118.35"
|
||||
height="118.35"
|
||||
rx="6.53"
|
||||
ry="6.53"
|
||||
/>
|
||||
<path
|
||||
id="f"
|
||||
fill="#fff"
|
||||
d="M86.48,123.17V77.34h15.38l2.3-17.86H86.48V48.08c0-5.17,1.44-8.7,8.85-8.7h9.46v-16A126.56,126.56,0,0,0,91,22.7C77.38,22.7,68,31,68,46.31V59.48H52.62V77.34H68v45.83Z"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
<div class={clsx("flex", "flex-col", "items-start", "space-y-1")}>
|
||||
{#if $session.user.connections.facebook}
|
||||
<p class={clsx("font-bold")}>Signed in as:</p>
|
||||
{$session.user.connections.facebook.name}
|
||||
{:else}
|
||||
<p class={clsx("font-bold")}>Not signed in</p>
|
||||
<a
|
||||
href="/api/auth/signin/facebook"
|
||||
class={clsx(
|
||||
"text-xs",
|
||||
"md:text-sm",
|
||||
"py-1",
|
||||
"px-2",
|
||||
"border-gray-400",
|
||||
"rounded",
|
||||
"hover:no-underline",
|
||||
"border",
|
||||
"hover:bg-gray-100",
|
||||
"transition-colors",
|
||||
"items-center",
|
||||
)}
|
||||
>
|
||||
Connect
|
||||
</a>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
<div class={clsx("flex", "items-center", "space-x-4")}>
|
||||
<svg viewBox="0 0 24 24" class={clsx("h-4", "w-4", "md:h-6", "md:w-6")}>
|
||||
<path
|
||||
fill="#FF4300"
|
||||
d="M14.5 15.41C14.58 15.5 14.58 15.69 14.5 15.8C13.77 16.5 12.41 16.56 12 16.56C11.61 16.56 10.25 16.5 9.54 15.8C9.44 15.69 9.44 15.5 9.54 15.41C9.65 15.31 9.82 15.31 9.92 15.41C10.38 15.87 11.33 16 12 16C12.69 16 13.66 15.87 14.1 15.41C14.21 15.31 14.38 15.31 14.5 15.41M10.75 13.04C10.75 12.47 10.28 12 9.71 12C9.14 12 8.67 12.47 8.67 13.04C8.67 13.61 9.14 14.09 9.71 14.08C10.28 14.08 10.75 13.61 10.75 13.04M14.29 12C13.72 12 13.25 12.5 13.25 13.05S13.72 14.09 14.29 14.09C14.86 14.09 15.33 13.61 15.33 13.05C15.33 12.5 14.86 12 14.29 12M22 12C22 17.5 17.5 22 12 22S2 17.5 2 12C2 6.5 6.5 2 12 2S22 6.5 22 12M18.67 12C18.67 11.19 18 10.54 17.22 10.54C16.82 10.54 16.46 10.7 16.2 10.95C15.2 10.23 13.83 9.77 12.3 9.71L12.97 6.58L15.14 7.05C15.16 7.6 15.62 8.04 16.18 8.04C16.75 8.04 17.22 7.57 17.22 7C17.22 6.43 16.75 5.96 16.18 5.96C15.77 5.96 15.41 6.2 15.25 6.55L12.82 6.03C12.75 6 12.68 6.03 12.63 6.07C12.57 6.11 12.54 6.17 12.53 6.24L11.79 9.72C10.24 9.77 8.84 10.23 7.82 10.96C7.56 10.71 7.2 10.56 6.81 10.56C6 10.56 5.35 11.21 5.35 12C5.35 12.61 5.71 13.11 6.21 13.34C6.19 13.5 6.18 13.62 6.18 13.78C6.18 16 8.79 17.85 12 17.85C15.23 17.85 17.85 16.03 17.85 13.78C17.85 13.64 17.84 13.5 17.81 13.34C18.31 13.11 18.67 12.6 18.67 12Z"
|
||||
/>
|
||||
</svg>
|
||||
<div class={clsx("flex", "flex-col", "items-start", "space-y-1")}>
|
||||
{#if $session.user.connections.reddit}
|
||||
<p class={clsx("font-bold")}>Signed in as:</p>
|
||||
{$session.user.connections.reddit.name}
|
||||
{:else}
|
||||
<p class={clsx("font-bold")}>Not signed in</p>
|
||||
<a
|
||||
href="/api/auth/signin/reddit"
|
||||
class={clsx(
|
||||
"text-xs",
|
||||
"md:text-sm",
|
||||
"py-1",
|
||||
"px-2",
|
||||
"border-gray-400",
|
||||
"rounded",
|
||||
"hover:no-underline",
|
||||
"border",
|
||||
"hover:bg-gray-100",
|
||||
"transition-colors",
|
||||
"items-center",
|
||||
)}
|
||||
>
|
||||
Connect
|
||||
</a>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
<div class={clsx("flex", "items-center", "space-x-4")}>
|
||||
<svg
|
||||
viewBox="0 0 128 128"
|
||||
class={clsx("h-4", "w-4", "md:h-6", "md:w-6", "filter", "grayscale", "opacity-60")}
|
||||
>
|
||||
<g id="surface1">
|
||||
<path
|
||||
style=" stroke:none;fill-rule:nonzero;fill:rgb(11.372549%,63.137255%,94.901961%);fill-opacity:1;"
|
||||
d="M 40.253906 127.636719 C 88.558594 127.636719 114.972656 78.679688 114.972656 36.234375 C 114.972656 34.84375 114.972656 33.457031 114.898438 32.078125 C 120.039062 27.53125 124.476562 21.898438 128 15.445312 C 123.210938 18.046875 118.128906 19.75 112.921875 20.507812 C 118.402344 16.488281 122.503906 10.171875 124.460938 2.734375 C 119.304688 6.476562 113.664062 9.113281 107.78125 10.535156 C 99.644531 -0.0507812 86.710938 -2.644531 76.234375 4.214844 C 65.761719 11.074219 60.351562 25.675781 63.035156 39.832031 C 41.921875 38.539062 22.246094 26.335938 8.914062 6.269531 C 1.933594 20.941406 5.488281 39.722656 17.023438 49.160156 C 12.875 48.988281 8.816406 47.605469 5.191406 45.128906 C 5.191406 45.265625 5.191406 45.402344 5.191406 45.539062 C 5.191406 60.8125 13.976562 73.976562 26.210938 77.03125 C 22.34375 78.320312 18.285156 78.503906 14.347656 77.574219 C 17.785156 90.667969 27.644531 99.644531 38.882812 99.902344 C 29.578125 108.820312 18.089844 113.652344 6.265625 113.621094 C 4.171875 113.621094 2.078125 113.472656 0 113.175781 C 12.007812 122.609375 25.980469 127.617188 40.253906 127.597656 "
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
<div class={clsx("flex", "flex-col", "items-start", "space-y-1")}>
|
||||
{#if $session.user.connections.twitter}
|
||||
<p class={clsx("font-bold")}>Signed in as:</p>
|
||||
|
||||
{$session.user.connections.twitter.name}
|
||||
{:else}
|
||||
<p class={clsx("font-bold")}>Not signed in</p>
|
||||
<div
|
||||
class={clsx(
|
||||
"text-xs",
|
||||
"md:text-sm",
|
||||
"py-1",
|
||||
"px-2",
|
||||
"border-gray-400",
|
||||
"rounded",
|
||||
"hover:no-underline",
|
||||
"border",
|
||||
"hover:bg-gray-100",
|
||||
"transition-colors",
|
||||
"items-center",
|
||||
"cursor-not-allowed",
|
||||
"inline-block",
|
||||
)}
|
||||
>
|
||||
Connect
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p class={clsx("text-lg", "mb-2")}>Session</p>
|
||||
<pre
|
||||
class={clsx(
|
||||
"bg-gray-100",
|
||||
"whitespace-pre-wrap",
|
||||
"p-3",
|
||||
)}>
|
||||
<code>{JSON.stringify($session, null, 2)}</code>
|
||||
</pre>
|
||||
</div>
|
12
app/static/logo.svg
Normal file
12
app/static/logo.svg
Normal file
@ -0,0 +1,12 @@
|
||||
<svg width="350" height="350" viewBox="0 0 350 350" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M252 131.25V141.75H262.5H273.438C285.725 141.75 295.75 151.766 295.75 164.062V317.188C295.75 329.484 285.725 339.5 273.438 339.5H76.5625C64.275 339.5 54.25 329.484 54.25 317.188V164.062C54.25 151.766 64.275 141.75 76.5625 141.75H87.5H98V131.25V87.5C98 45.0415 132.542 10.5 175 10.5C217.458 10.5 252 45.0415 252 87.5V131.25ZM233.332 141.75H243.832V131.25V87.5C243.832 49.5294 212.971 18.6676 175 18.6676C137.029 18.6676 106.168 49.5294 106.168 87.5V131.25V141.75H116.668H233.332Z" stroke="#F73D00" stroke-width="21"/>
|
||||
<g clip-path="url(#clip0)">
|
||||
<path d="M226.422 190.641C213.311 171.88 187.209 166.384 168.444 178.213L135.366 199.244C126.344 204.86 120.089 214.061 118.285 224.457C116.721 233.18 118.045 242.141 122.255 249.908C119.368 254.21 117.443 258.99 116.601 264.008C114.677 274.643 117.203 285.636 123.457 294.359C136.689 313.119 162.67 318.616 181.435 306.786L214.513 285.875C223.535 280.259 229.79 271.058 231.594 260.663C233.158 251.94 231.835 242.978 227.625 235.211C230.511 230.909 232.436 226.13 233.278 221.111C235.323 210.357 232.797 199.363 226.422 190.641Z" fill="#FF3E00"/>
|
||||
<path d="M165.197 296.152C154.491 298.9 143.305 294.718 137.05 285.756C133.201 280.498 131.757 273.926 132.84 267.474C133.081 266.398 133.321 265.442 133.562 264.367L134.163 262.455L135.847 263.65C139.817 266.518 144.147 268.669 148.838 270.103L150.041 270.461L149.92 271.656C149.8 273.329 150.281 275.121 151.244 276.555C153.168 279.303 156.536 280.618 159.784 279.781C160.506 279.542 161.227 279.303 161.829 278.945L194.787 258.034C196.471 256.958 197.554 255.405 197.914 253.493C198.275 251.581 197.794 249.55 196.712 247.997C194.787 245.248 191.419 244.053 188.171 244.89C187.45 245.129 186.728 245.368 186.126 245.726L173.496 253.732C171.452 255.047 169.166 256.003 166.76 256.6C156.055 259.348 144.868 255.166 138.614 246.204C134.885 240.947 133.321 234.375 134.524 227.922C135.607 221.708 139.456 216.092 144.869 212.747L177.947 191.836C179.992 190.521 182.277 189.565 184.683 188.848C195.388 186.1 206.575 190.282 212.83 199.244C216.679 204.502 218.122 211.074 217.04 217.526C216.799 218.602 216.559 219.558 216.198 220.633L215.596 222.545L213.912 221.35C209.943 218.482 205.613 216.331 200.922 214.897L199.719 214.539L199.839 213.344C199.959 211.671 199.478 209.879 198.516 208.445C196.591 205.697 193.223 204.502 189.976 205.338C189.254 205.577 188.532 205.816 187.931 206.175L154.972 227.086C153.288 228.161 152.206 229.714 151.845 231.626C151.484 233.538 151.965 235.57 153.048 237.123C154.972 239.871 158.34 241.066 161.588 240.23C162.31 239.991 163.032 239.752 163.633 239.393L176.263 231.387C178.308 230.073 180.593 229.117 182.999 228.4C193.704 225.652 204.891 229.834 211.146 238.796C214.995 244.053 216.438 250.625 215.356 257.078C214.273 263.292 210.424 268.908 205.011 272.253L171.933 293.164C169.888 294.479 167.602 295.435 165.197 296.152Z" fill="white"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0">
|
||||
<rect width="118" height="141" fill="white" transform="translate(116 172)"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
After Width: | Height: | Size: 3.0 KiB |
19
app/svelte.config.js
Normal file
19
app/svelte.config.js
Normal file
@ -0,0 +1,19 @@
|
||||
import preprocess from "svelte-preprocess";
|
||||
|
||||
/** @type {import('@sveltejs/kit').Config} */
|
||||
const config = {
|
||||
// Consult https://github.com/sveltejs/svelte-preprocess
|
||||
// for more information about preprocessors
|
||||
preprocess: [
|
||||
preprocess({
|
||||
postcss: true,
|
||||
}),
|
||||
],
|
||||
|
||||
kit: {
|
||||
// hydrate the <div id="svelte"> element in src/app.html
|
||||
target: "#svelte",
|
||||
},
|
||||
};
|
||||
|
||||
export default config;
|
48
app/tailwind.config.cjs
Normal file
48
app/tailwind.config.cjs
Normal file
@ -0,0 +1,48 @@
|
||||
const { tailwindExtractor } = require("tailwindcss/lib/lib/purgeUnusedStyles");
|
||||
const { fontFamily } = require("tailwindcss/defaultTheme");
|
||||
const colors = require("tailwindcss/colors");
|
||||
|
||||
module.exports = {
|
||||
mode: "aot",
|
||||
purge: {
|
||||
content: ["./src/**/*.{html,js,svelte,ts}"],
|
||||
options: {
|
||||
defaultExtractor: (content) => [
|
||||
// If this stops working, please open an issue at https://github.com/svelte-add/tailwindcss/issues rather than bothering Tailwind Labs about it
|
||||
...tailwindExtractor(content),
|
||||
// Match Svelte class: directives (https://github.com/tailwindlabs/tailwindcss/discussions/1731)
|
||||
...[...content.matchAll(/(?:class:)*([\w\d-/:%.]+)/gm)].map(
|
||||
([_match, group, ..._rest]) => group,
|
||||
),
|
||||
],
|
||||
},
|
||||
safelist: [/^svelte-[\d\w]+$/],
|
||||
},
|
||||
theme: {
|
||||
extend: {
|
||||
fontFamily: {
|
||||
sans: ["Inter", ...fontFamily.sans],
|
||||
serif: [...fontFamily.serif],
|
||||
mono: ["Fira Mono", ...fontFamily.mono],
|
||||
},
|
||||
colors: {
|
||||
transparent: "transparent",
|
||||
current: "currentColor",
|
||||
black: colors.black,
|
||||
white: colors.white,
|
||||
gray: colors.trueGray,
|
||||
"cool-gray": colors.blueGray,
|
||||
blue: colors.blue,
|
||||
indigo: colors.indigo,
|
||||
red: colors.rose,
|
||||
orange: colors.orange,
|
||||
yellow: colors.amber,
|
||||
pink: colors.pink,
|
||||
},
|
||||
},
|
||||
},
|
||||
variants: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [],
|
||||
};
|
30
app/tsconfig.json
Normal file
30
app/tsconfig.json
Normal file
@ -0,0 +1,30 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"moduleResolution": "node",
|
||||
"module": "es2020",
|
||||
"lib": ["es2020"],
|
||||
"target": "es2019",
|
||||
/**
|
||||
svelte-preprocess cannot figure out whether you have a value or a type, so tell TypeScript
|
||||
to enforce using \`import type\` instead of \`import\` for Types.
|
||||
*/
|
||||
"importsNotUsedAsValues": "error",
|
||||
"isolatedModules": true,
|
||||
"resolveJsonModule": true,
|
||||
/**
|
||||
To have warnings/errors of the Svelte compiler at the correct position,
|
||||
enable source maps by default.
|
||||
*/
|
||||
"sourceMap": true,
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"baseUrl": ".",
|
||||
"allowJs": true,
|
||||
"checkJs": true,
|
||||
"paths": {
|
||||
"$lib/*": ["src/lib/*"]
|
||||
}
|
||||
},
|
||||
"include": ["src/**/*.d.ts", "src/**/*.js", "src/**/*.ts", "src/**/*.svelte"]
|
||||
}
|
2404
app/yarn.lock
Normal file
2404
app/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
32
package.json
32
package.json
@ -1,24 +1,48 @@
|
||||
{
|
||||
"name": "SvelteKitAuth",
|
||||
"name": "svelte-kit-auth",
|
||||
"version": "1.0.0",
|
||||
"description": "Authentication library for use with SvelteKit featuring built-in OAuth providers and zero restriction customization!",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"exports": {
|
||||
".": "./dist/index.js",
|
||||
"./client": "./dist/client/index.js",
|
||||
"./providers": "./dist/providers/index.js"
|
||||
},
|
||||
"typesVersions": {
|
||||
"*": {
|
||||
"client": [
|
||||
"./dist/client/index.d.ts"
|
||||
],
|
||||
"providers": [
|
||||
"./dist/providers/index.d.ts"
|
||||
]
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"build:watch": "tsc --watch",
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"lint": "prettier --check --plugin-search-dir=. . && eslint --ignore-path .gitignore .",
|
||||
"format": "prettier --write --plugin-search-dir=. ."
|
||||
},
|
||||
"keywords": [
|
||||
"auth",
|
||||
"authentication",
|
||||
"csrf",
|
||||
"jwt",
|
||||
"nodejs",
|
||||
"oauth",
|
||||
"oauth2",
|
||||
"oidc",
|
||||
"sveltejs",
|
||||
"sveltekit",
|
||||
"auth",
|
||||
"oauth"
|
||||
"sveltekitauth"
|
||||
],
|
||||
"author": "RaviAnand Mohabir <moravrav@gmail.com> (https://ravianand.web.app)",
|
||||
"repository": "https://github.com/Dan6erbond/SvelteKitAuth",
|
||||
"license": "MIT",
|
||||
"private": false,
|
||||
"types": "dist/index.d.ts",
|
||||
"dependencies": {
|
||||
"cookie": "^0.4.1",
|
||||
"jsonwebtoken": "^8.5.1"
|
||||
|
12
res/logo.svg
Normal file
12
res/logo.svg
Normal file
@ -0,0 +1,12 @@
|
||||
<svg width="350" height="350" viewBox="0 0 350 350" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M252 131.25V141.75H262.5H273.438C285.725 141.75 295.75 151.766 295.75 164.062V317.188C295.75 329.484 285.725 339.5 273.438 339.5H76.5625C64.275 339.5 54.25 329.484 54.25 317.188V164.062C54.25 151.766 64.275 141.75 76.5625 141.75H87.5H98V131.25V87.5C98 45.0415 132.542 10.5 175 10.5C217.458 10.5 252 45.0415 252 87.5V131.25ZM233.332 141.75H243.832V131.25V87.5C243.832 49.5294 212.971 18.6676 175 18.6676C137.029 18.6676 106.168 49.5294 106.168 87.5V131.25V141.75H116.668H233.332Z" stroke="#F73D00" stroke-width="21"/>
|
||||
<g clip-path="url(#clip0)">
|
||||
<path d="M226.422 190.641C213.311 171.88 187.209 166.384 168.444 178.213L135.366 199.244C126.344 204.86 120.089 214.061 118.285 224.457C116.721 233.18 118.045 242.141 122.255 249.908C119.368 254.21 117.443 258.99 116.601 264.008C114.677 274.643 117.203 285.636 123.457 294.359C136.689 313.119 162.67 318.616 181.435 306.786L214.513 285.875C223.535 280.259 229.79 271.058 231.594 260.663C233.158 251.94 231.835 242.978 227.625 235.211C230.511 230.909 232.436 226.13 233.278 221.111C235.323 210.357 232.797 199.363 226.422 190.641Z" fill="#FF3E00"/>
|
||||
<path d="M165.197 296.152C154.491 298.9 143.305 294.718 137.05 285.756C133.201 280.498 131.757 273.926 132.84 267.474C133.081 266.398 133.321 265.442 133.562 264.367L134.163 262.455L135.847 263.65C139.817 266.518 144.147 268.669 148.838 270.103L150.041 270.461L149.92 271.656C149.8 273.329 150.281 275.121 151.244 276.555C153.168 279.303 156.536 280.618 159.784 279.781C160.506 279.542 161.227 279.303 161.829 278.945L194.787 258.034C196.471 256.958 197.554 255.405 197.914 253.493C198.275 251.581 197.794 249.55 196.712 247.997C194.787 245.248 191.419 244.053 188.171 244.89C187.45 245.129 186.728 245.368 186.126 245.726L173.496 253.732C171.452 255.047 169.166 256.003 166.76 256.6C156.055 259.348 144.868 255.166 138.614 246.204C134.885 240.947 133.321 234.375 134.524 227.922C135.607 221.708 139.456 216.092 144.869 212.747L177.947 191.836C179.992 190.521 182.277 189.565 184.683 188.848C195.388 186.1 206.575 190.282 212.83 199.244C216.679 204.502 218.122 211.074 217.04 217.526C216.799 218.602 216.559 219.558 216.198 220.633L215.596 222.545L213.912 221.35C209.943 218.482 205.613 216.331 200.922 214.897L199.719 214.539L199.839 213.344C199.959 211.671 199.478 209.879 198.516 208.445C196.591 205.697 193.223 204.502 189.976 205.338C189.254 205.577 188.532 205.816 187.931 206.175L154.972 227.086C153.288 228.161 152.206 229.714 151.845 231.626C151.484 233.538 151.965 235.57 153.048 237.123C154.972 239.871 158.34 241.066 161.588 240.23C162.31 239.991 163.032 239.752 163.633 239.393L176.263 231.387C178.308 230.073 180.593 229.117 182.999 228.4C193.704 225.652 204.891 229.834 211.146 238.796C214.995 244.053 216.438 250.625 215.356 257.078C214.273 263.292 210.424 268.908 205.011 272.253L171.933 293.164C169.888 294.479 167.602 295.435 165.197 296.152Z" fill="white"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0">
|
||||
<rect width="118" height="141" fill="white" transform="translate(116 172)"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
After Width: | Height: | Size: 3.0 KiB |
32
src/auth.ts
32
src/auth.ts
@ -4,13 +4,16 @@ import type { Headers } from "@sveltejs/kit/types/helper";
|
||||
import cookie from "cookie";
|
||||
import * as jsonwebtoken from "jsonwebtoken";
|
||||
import type { JWT, Session } from "./interfaces";
|
||||
import { join } from "./path";
|
||||
import type { Provider } from "./providers";
|
||||
|
||||
interface AuthConfig {
|
||||
providers?: Provider[];
|
||||
providers: Provider[];
|
||||
callbacks?: AuthCallbacks;
|
||||
jwtSecret?: string;
|
||||
jwtExpiresIn?: string | number;
|
||||
host?: string;
|
||||
basePath?: string;
|
||||
}
|
||||
|
||||
interface AuthCallbacks {
|
||||
@ -61,8 +64,13 @@ export class Auth {
|
||||
return token;
|
||||
}
|
||||
|
||||
getBaseUrl(host: string) {
|
||||
return `http://${host}`;
|
||||
getBaseUrl(host?: string) {
|
||||
return this.config?.host ?? `http://${host}`;
|
||||
}
|
||||
|
||||
getPath(path: string, host?: string) {
|
||||
const uri = join([this.config?.basePath ?? "/api/auth", path]);
|
||||
return new URL(uri, this.getBaseUrl(host)).pathname;
|
||||
}
|
||||
|
||||
setToken(headers: Headers, newToken: JWT | any) {
|
||||
@ -107,8 +115,7 @@ export class Auth {
|
||||
}
|
||||
|
||||
const jwt = this.signToken(token);
|
||||
console.log(jwt);
|
||||
const redirect = await this.getRedirectUrl(host, redirectUrl);
|
||||
const redirect = await this.getRedirectUrl(host, redirectUrl ?? undefined);
|
||||
|
||||
return {
|
||||
status: 302,
|
||||
@ -122,7 +129,7 @@ export class Auth {
|
||||
async handleEndpoint(request: ServerRequest): Promise<EndpointOutput> {
|
||||
const { path, headers, method, host } = request;
|
||||
|
||||
if (path === "/api/auth/signout") {
|
||||
if (path === this.getPath("signout")) {
|
||||
const token = this.setToken(headers, {});
|
||||
const jwt = this.signToken(token);
|
||||
|
||||
@ -150,9 +157,9 @@ export class Auth {
|
||||
|
||||
const match = path.match(/\/api\/auth\/(?<method>signin|callback)\/(?<provider>\w+)/);
|
||||
|
||||
if (match) {
|
||||
if (match && match.groups) {
|
||||
const provider = this.config?.providers?.find(
|
||||
(provider) => provider.id === match.groups.provider,
|
||||
(provider) => provider.id === match.groups!.provider,
|
||||
);
|
||||
if (provider) {
|
||||
if (match.groups.method === "signin") {
|
||||
@ -162,14 +169,19 @@ export class Auth {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
status: 404,
|
||||
body: "Not found.",
|
||||
};
|
||||
}
|
||||
|
||||
get: RequestHandler = async (request) => {
|
||||
const { path } = request;
|
||||
|
||||
if (path === "/api/auth/csrf") {
|
||||
if (path === this.getPath("csrf")) {
|
||||
return { body: "1234" }; // TODO: Generate real token
|
||||
} else if (path === "/api/auth/session") {
|
||||
} else if (path === this.getPath("session")) {
|
||||
const session = await this.getSession(request);
|
||||
return {
|
||||
body: {
|
||||
|
@ -19,17 +19,19 @@ export async function signIn(provider: string, data?: any, config?: SignInConfig
|
||||
return await res.json();
|
||||
}
|
||||
|
||||
let redirectUrl: string;
|
||||
let redirectUrl: string | undefined;
|
||||
if (config?.redirectUrl) {
|
||||
redirectUrl = config.redirectUrl;
|
||||
} else {
|
||||
let $val: Page;
|
||||
let $val: Page | undefined;
|
||||
page.subscribe(($) => ($val = $))();
|
||||
redirectUrl = `${$val.host}${$val.path}?${$val.query}`;
|
||||
if ($val) {
|
||||
redirectUrl = `${$val.host}${$val.path}?${$val.query}`;
|
||||
}
|
||||
}
|
||||
|
||||
const queryData = {
|
||||
redirect: redirectUrl,
|
||||
redirect: redirectUrl ?? "/",
|
||||
};
|
||||
const query = new URLSearchParams(queryData);
|
||||
const path = `/api/auth/login/${provider}?${query}`;
|
||||
|
@ -1,4 +1,7 @@
|
||||
export { Auth } from "./auth";
|
||||
import { Auth } from "./auth";
|
||||
|
||||
export { JWT, Session, User } from "./interfaces";
|
||||
export { Provider } from "./providers";
|
||||
export { CallbackResult, Profile } from "./types";
|
||||
|
||||
export default Auth;
|
||||
|
0
src/jwt.ts
Normal file
0
src/jwt.ts
Normal file
5
src/path.ts
Normal file
5
src/path.ts
Normal file
@ -0,0 +1,5 @@
|
||||
export function join(parts: string[], sep = "/") {
|
||||
const separator = sep || "/";
|
||||
const replace = new RegExp(separator + "{1,}", "g");
|
||||
return parts.join(separator).replace(replace, separator);
|
||||
}
|
@ -11,7 +11,7 @@ export abstract class Provider<T extends ProviderConfig = ProviderConfig> {
|
||||
id: string;
|
||||
|
||||
constructor(protected readonly config: T) {
|
||||
this.id = config.id;
|
||||
this.id = config.id!;
|
||||
}
|
||||
|
||||
getUri(host: string, path: string) {
|
||||
|
@ -28,7 +28,7 @@ export class FacebookAuthProvider extends OAuth2Provider<FacebookAuthProviderCon
|
||||
|
||||
const data = {
|
||||
client_id: this.config.clientId,
|
||||
scope: this.config.scope,
|
||||
scope: this.config.scope!,
|
||||
redirect_uri: this.getCallbackUri(host),
|
||||
state,
|
||||
};
|
||||
@ -69,7 +69,7 @@ export class FacebookAuthProvider extends OAuth2Provider<FacebookAuthProviderCon
|
||||
|
||||
const data = {
|
||||
access_token: tokens.access_token,
|
||||
fields: this.config.userProfileFields,
|
||||
fields: this.config.userProfileFields!,
|
||||
};
|
||||
|
||||
const res = await fetch(`${endpoint}?${new URLSearchParams(data)}`);
|
||||
|
@ -23,7 +23,7 @@ export class GoogleOAuthProvider extends OAuth2Provider<GoogleOAuthProviderConfi
|
||||
}
|
||||
|
||||
async getProviderMetadata() {
|
||||
const res = await fetch(this.config.discoveryDocument);
|
||||
const res = await fetch(this.config.discoveryDocument!);
|
||||
const metadata = await res.json();
|
||||
return metadata;
|
||||
}
|
||||
@ -39,7 +39,7 @@ export class GoogleOAuthProvider extends OAuth2Provider<GoogleOAuthProviderConfi
|
||||
const data = {
|
||||
response_type: "code",
|
||||
client_id: this.config.clientId,
|
||||
scope: this.config.scope,
|
||||
scope: this.config.scope!,
|
||||
redirect_uri: this.getCallbackUri(host),
|
||||
state,
|
||||
login_hint: "example@provider.com",
|
||||
|
@ -1,2 +1,6 @@
|
||||
export { Provider } from "./base";
|
||||
export { GoogleOAuthProvider } from "./google";
|
||||
export { TwitterAuthProvider } from "./twitter";
|
||||
export { FacebookAuthProvider } from "./facebook";
|
||||
export { OAuth2Provider } from "./oauth2";
|
||||
export { RedditOAuthProvider } from "./reddit";
|
||||
|
@ -35,7 +35,7 @@ export abstract class OAuth2Provider<T extends OAuth2ProviderConfig> extends Pro
|
||||
|
||||
getStateValue(query: URLSearchParams, name: string) {
|
||||
if (query.get("state")) {
|
||||
const state = Buffer.from(query.get("state"), "base64").toString();
|
||||
const state = Buffer.from(query.get("state")!, "base64").toString();
|
||||
return state
|
||||
.split(",")
|
||||
.find((state) => state.startsWith(`${name}=`))
|
||||
@ -47,7 +47,7 @@ export abstract class OAuth2Provider<T extends OAuth2ProviderConfig> extends Pro
|
||||
const code = query.get("code");
|
||||
const redirect = this.getStateValue(query, "redirect");
|
||||
|
||||
const tokens = await this.getTokens(code, this.getCallbackUri(host));
|
||||
const tokens = await this.getTokens(code!, this.getCallbackUri(host));
|
||||
let user = await this.getUserProfile(tokens);
|
||||
|
||||
if (this.config.profile) {
|
||||
|
@ -8,55 +8,57 @@ interface RedditOAuthProviderConfig extends OAuth2ProviderConfig {
|
||||
duration?: "temporary" | "permanent";
|
||||
}
|
||||
|
||||
const redditProfileHandler = ({
|
||||
is_employee,
|
||||
has_external_account,
|
||||
snoovatar_img,
|
||||
verified,
|
||||
id,
|
||||
over_18,
|
||||
is_gold,
|
||||
is_mod,
|
||||
awarder_karma,
|
||||
has_verified_email,
|
||||
is_suspended,
|
||||
icon_img,
|
||||
pref_nightmode,
|
||||
awardee_karma,
|
||||
password_set,
|
||||
link_karma,
|
||||
total_karma,
|
||||
name,
|
||||
created,
|
||||
created_utc,
|
||||
comment_karma,
|
||||
}) => ({
|
||||
is_employee,
|
||||
has_external_account,
|
||||
snoovatar_img,
|
||||
verified,
|
||||
id,
|
||||
over_18,
|
||||
is_gold,
|
||||
is_mod,
|
||||
awarder_karma,
|
||||
has_verified_email,
|
||||
is_suspended,
|
||||
icon_img,
|
||||
pref_nightmode,
|
||||
awardee_karma,
|
||||
password_set,
|
||||
link_karma,
|
||||
total_karma,
|
||||
name,
|
||||
created,
|
||||
created_utc,
|
||||
comment_karma,
|
||||
});
|
||||
|
||||
const defaultConfig: Partial<RedditOAuthProviderConfig> = {
|
||||
id: "reddit",
|
||||
scope: "identity",
|
||||
duration: "temporary",
|
||||
profile: ({
|
||||
is_employee,
|
||||
has_external_account,
|
||||
snoovatar_img,
|
||||
verified,
|
||||
id,
|
||||
over_18,
|
||||
is_gold,
|
||||
is_mod,
|
||||
awarder_karma,
|
||||
has_verified_email,
|
||||
is_suspended,
|
||||
icon_img,
|
||||
pref_nightmode,
|
||||
awardee_karma,
|
||||
password_set,
|
||||
link_karma,
|
||||
total_karma,
|
||||
name,
|
||||
created,
|
||||
created_utc,
|
||||
comment_karma,
|
||||
}) => ({
|
||||
is_employee,
|
||||
has_external_account,
|
||||
snoovatar_img,
|
||||
verified,
|
||||
id,
|
||||
over_18,
|
||||
is_gold,
|
||||
is_mod,
|
||||
awarder_karma,
|
||||
has_verified_email,
|
||||
is_suspended,
|
||||
icon_img,
|
||||
pref_nightmode,
|
||||
awardee_karma,
|
||||
password_set,
|
||||
link_karma,
|
||||
total_karma,
|
||||
name,
|
||||
created,
|
||||
created_utc,
|
||||
comment_karma,
|
||||
}),
|
||||
profile: redditProfileHandler,
|
||||
};
|
||||
|
||||
export class RedditOAuthProvider extends OAuth2Provider<RedditOAuthProviderConfig> {
|
||||
@ -67,6 +69,8 @@ export class RedditOAuthProvider extends OAuth2Provider<RedditOAuthProviderConfi
|
||||
});
|
||||
}
|
||||
|
||||
static profileHandler = redditProfileHandler;
|
||||
|
||||
async getSigninUrl({ host }: ServerRequest, state: string) {
|
||||
const endpoint = "https://www.reddit.com/api/v1/authorize";
|
||||
|
||||
@ -75,8 +79,8 @@ export class RedditOAuthProvider extends OAuth2Provider<RedditOAuthProviderConfi
|
||||
response_type: "code",
|
||||
state,
|
||||
redirect_uri: this.getCallbackUri(host),
|
||||
duration: this.config.duration,
|
||||
scope: this.config.scope,
|
||||
duration: this.config.duration!,
|
||||
scope: this.config.scope!,
|
||||
};
|
||||
|
||||
const url = `${endpoint}?${new URLSearchParams(data)}`;
|
||||
|
@ -75,7 +75,7 @@ export class TwitterAuthProvider extends OAuth2Provider<TwitterAuthProviderConfi
|
||||
const oauthVerifier = query.get("oauth_verifier");
|
||||
const redirect = this.getStateValue(query, "redirect");
|
||||
|
||||
const tokens = await this.getTokens(oauthToken, oauthVerifier);
|
||||
const tokens = await this.getTokens(oauthToken!, oauthVerifier!);
|
||||
let user = await this.getUserProfile(tokens);
|
||||
|
||||
if (this.config.profile) {
|
||||
|
@ -1,20 +1,21 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"module": "commonjs",
|
||||
"target": "es2017",
|
||||
"module": "es6",
|
||||
"moduleResolution": "node",
|
||||
"strict": true,
|
||||
"noImplicitAny": false,
|
||||
"declaration": true,
|
||||
"removeComments": true,
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true,
|
||||
"skipLibCheck": true,
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"target": "es2017",
|
||||
"sourceMap": true,
|
||||
"outDir": "./dist",
|
||||
"baseUrl": "./",
|
||||
"incremental": true
|
||||
},
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"dist"
|
||||
]
|
||||
"exclude": ["node_modules", "dist", "app"]
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user