├── .gitignore ├── .vscode └── settings.json ├── README.md ├── apps └── dev │ ├── .gitignore │ ├── .npmrc │ ├── .stackblitzrc │ ├── .vscode │ ├── extensions.json │ └── launch.json │ ├── README.md │ ├── astro.config.mjs │ ├── package.json │ ├── public │ └── favicon.ico │ ├── sandbox.config.json │ ├── src │ ├── components │ │ ├── IntroSection │ │ │ ├── index.module.css │ │ │ └── index.tsx │ │ └── UserStore │ │ │ └── index.tsx │ ├── env.d.ts │ ├── modules.d.ts │ └── pages │ │ ├── api │ │ ├── auth │ │ │ └── [...astroauth].ts │ │ └── privateRoute.ts │ │ ├── dashboard.astro │ │ └── index.astro │ └── tsconfig.json ├── docs ├── .gitignore ├── .npmrc ├── .stackblitzrc ├── .vscode │ ├── extensions.json │ └── launch.json ├── astro.config.mjs ├── package.json ├── public │ ├── astro.png │ ├── astroauth │ │ ├── astroauth-tp-b.png │ │ ├── astroauth-tp.png │ │ └── astroauth.png │ └── make-scrollable-code-focusable.js ├── sandbox.config.json ├── src │ ├── components │ │ ├── Footer │ │ │ ├── AvatarList.astro │ │ │ └── Footer.astro │ │ ├── HeadCommon.astro │ │ ├── HeadSEO.astro │ │ ├── Header │ │ │ ├── Header.astro │ │ │ ├── LanguageSelect.css │ │ │ ├── LanguageSelect.tsx │ │ │ ├── Search.css │ │ │ ├── Search.tsx │ │ │ ├── SidebarToggle.tsx │ │ │ └── SkipToContent.astro │ │ ├── LeftSidebar │ │ │ └── LeftSidebar.astro │ │ ├── PageContent │ │ │ └── PageContent.astro │ │ └── RightSidebar │ │ │ ├── MoreMenu.astro │ │ │ ├── RightSidebar.astro │ │ │ ├── TableOfContents.tsx │ │ │ ├── ThemeToggleButton.css │ │ │ └── ThemeToggleButton.tsx │ ├── config.ts │ ├── env.d.ts │ ├── languages.ts │ ├── layouts │ │ └── MainLayout.astro │ ├── pages │ │ ├── client │ │ │ ├── signIn.md │ │ │ ├── signOut.md │ │ │ └── state-store.astro │ │ ├── core │ │ │ ├── get-error.md │ │ │ ├── get-user.md │ │ │ └── redirect-user.md │ │ ├── extra │ │ │ └── database-integration.md │ │ ├── getting-started │ │ │ ├── astro-auth-cli.md │ │ │ ├── getting-started.md │ │ │ ├── introduction.md │ │ │ ├── typescript-support.md │ │ │ └── using-astro-auth.md │ │ ├── hooks │ │ │ ├── account.md │ │ │ ├── jwt.md │ │ │ ├── redirectError.md │ │ │ └── sign-in.md │ │ ├── index.astro │ │ ├── providers │ │ │ ├── credentials.md │ │ │ ├── metamask.md │ │ │ └── oauth.md │ │ ├── state-store │ │ │ ├── other-frameworks.md │ │ │ └── react.md │ │ └── ui-library │ │ │ ├── button.md │ │ │ ├── instructions.md │ │ │ ├── provider-buttons.md │ │ │ └── title.md │ └── styles │ │ ├── index.css │ │ └── theme.css ├── tsconfig.json └── yarn.lock ├── package.json ├── packages ├── astro-auth-cli │ ├── README.md │ ├── package.json │ ├── src │ │ └── index.ts │ └── tsconfig.json ├── astro-auth-client │ ├── README.md │ ├── package.json │ ├── rollup.config.js │ ├── src │ │ ├── env.d.ts │ │ ├── index.ts │ │ ├── signIn │ │ │ └── index.ts │ │ ├── signOut │ │ │ └── index.ts │ │ └── stateStores │ │ │ ├── ReactStateStore │ │ │ └── index.tsx │ │ │ └── store │ │ │ ├── useUser.ts │ │ │ └── userStore.ts │ └── tsconfig.json ├── astro-auth-core │ ├── README.md │ ├── package.json │ ├── rollup.config.js │ ├── src │ │ ├── AstroAuth │ │ │ ├── handlers │ │ │ │ ├── getMessageToSign.ts │ │ │ │ ├── getServerUser.ts │ │ │ │ ├── index.ts │ │ │ │ ├── oauthCallback.ts │ │ │ │ ├── signIn.ts │ │ │ │ └── signout.ts │ │ │ └── index.ts │ │ ├── env.d.ts │ │ ├── getError │ │ │ └── index.ts │ │ ├── getUser │ │ │ └── index.ts │ │ ├── index.ts │ │ ├── lib │ │ │ └── oauth │ │ │ │ ├── client.ts │ │ │ │ ├── getUserDetails.ts │ │ │ │ ├── pkceUtils.ts │ │ │ │ └── stateUtils.ts │ │ └── utils │ │ │ ├── getURLSlash.ts │ │ │ └── parseCookieString.ts │ └── tsconfig.json ├── astro-auth-providers │ ├── README.md │ ├── package.json │ ├── src │ │ ├── env.d.ts │ │ ├── index.ts │ │ └── providers │ │ │ ├── CredentialProvider.ts │ │ │ ├── DiscordProvider.ts │ │ │ ├── FacebookProvider.ts │ │ │ ├── GithubProvider.ts │ │ │ ├── GoogleProvider.ts │ │ │ ├── InstagramProvider.ts │ │ │ ├── MetamaskProvider.ts │ │ │ ├── SpotifyProvider.ts │ │ │ ├── TwitterProvider.ts │ │ │ └── ZoomProvider.ts │ └── tsconfig.json ├── astro-auth-types │ ├── package.json │ └── src │ │ └── index.ts └── astro-auth-ui │ ├── README.md │ ├── index.ts │ ├── package.json │ ├── src │ ├── components │ │ ├── Button │ │ │ ├── index.module.css │ │ │ └── index.tsx │ │ ├── DiscordButton │ │ │ └── index.tsx │ │ ├── FacebookButton │ │ │ └── index.tsx │ │ ├── GithubButton │ │ │ └── index.tsx │ │ ├── GoogleButton │ │ │ └── index.tsx │ │ ├── InstagramButton │ │ │ └── index.tsx │ │ ├── MetamaskButton │ │ │ └── index.tsx │ │ ├── SpotifyButton │ │ │ └── index.tsx │ │ ├── Title │ │ │ ├── index.module.css │ │ │ └── index.tsx │ │ ├── TwitterButton │ │ │ └── index.tsx │ │ ├── ZoomButton │ │ │ └── index.tsx │ │ └── socialButtons.module.css │ ├── index.css │ ├── index.ts │ └── modules.d.ts │ └── tsconfig.json └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | .pnpm-debug.log* 9 | 10 | # Diagnostic reports (https://nodejs.org/api/report.html) 11 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 12 | 13 | # Runtime data 14 | pids 15 | *.pid 16 | *.seed 17 | *.pid.lock 18 | 19 | # Directory for instrumented libs generated by jscoverage/JSCover 20 | lib-cov 21 | 22 | # Coverage directory used by tools like istanbul 23 | coverage 24 | *.lcov 25 | 26 | # nyc test coverage 27 | .nyc_output 28 | 29 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 30 | .grunt 31 | 32 | # Bower dependency directory (https://bower.io/) 33 | bower_components 34 | 35 | # node-waf configuration 36 | .lock-wscript 37 | 38 | # Compiled binary addons (https://nodejs.org/api/addons.html) 39 | build/Release 40 | 41 | # Dependency directories 42 | node_modules/ 43 | jspm_packages/ 44 | 45 | # Snowpack dependency directory (https://snowpack.dev/) 46 | web_modules/ 47 | 48 | # TypeScript cache 49 | *.tsbuildinfo 50 | 51 | # Optional npm cache directory 52 | .npm 53 | 54 | # Optional eslint cache 55 | .eslintcache 56 | 57 | # Optional stylelint cache 58 | .stylelintcache 59 | 60 | # Microbundle cache 61 | .rpt2_cache/ 62 | .rts2_cache_cjs/ 63 | .rts2_cache_es/ 64 | .rts2_cache_umd/ 65 | 66 | # Optional REPL history 67 | .node_repl_history 68 | 69 | # Output of 'npm pack' 70 | *.tgz 71 | 72 | # Yarn Integrity file 73 | .yarn-integrity 74 | 75 | # dotenv environment variable files 76 | .env 77 | .env.development.local 78 | .env.test.local 79 | .env.production.local 80 | .env.local 81 | 82 | # parcel-bundler cache (https://parceljs.org/) 83 | .cache 84 | .parcel-cache 85 | 86 | # Next.js build output 87 | .next 88 | out 89 | 90 | # Nuxt.js build / generate output 91 | .nuxt 92 | dist 93 | 94 | # Gatsby files 95 | .cache/ 96 | # Comment in the public line in if your project uses Gatsby and not Next.js 97 | # https://nextjs.org/blog/next-9-1#public-directory-support 98 | # public 99 | 100 | # vuepress build output 101 | .vuepress/dist 102 | 103 | # vuepress v2.x temp and cache directory 104 | .temp 105 | .cache 106 | 107 | # Docusaurus cache and generated files 108 | .docusaurus 109 | 110 | # Serverless directories 111 | .serverless/ 112 | 113 | # FuseBox cache 114 | .fusebox/ 115 | 116 | # DynamoDB Local files 117 | .dynamodb/ 118 | 119 | # TernJS port file 120 | .tern-port 121 | 122 | # Stores VSCode versions used for testing VSCode extensions 123 | .vscode-test 124 | 125 | # yarn v2 126 | .yarn/cache 127 | .yarn/unplugged 128 | .yarn/build-state.yml 129 | .yarn/install-state.gz 130 | .pnp.* 131 | 132 | # Codeql 133 | astro-auth-database 134 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "cSpell.words": [ 3 | "astro", 4 | "astroauth", 5 | "astrojs", 6 | "behaviour", 7 | "builtins", 8 | "Inpage", 9 | "nanostores", 10 | "pkce", 11 | "preact", 12 | "redaxios", 13 | "signin", 14 | "signout", 15 | "userinfo" 16 | ], 17 | "discord.enabled": false 18 | } 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 |

Astro Auth

4 | 5 |

6 | 7 |

8 | 9 | ## Getting Started 10 | 11 | Run one of the following command inside your project directory to install the package: 12 | 13 | ``` 14 | yarn add @astro-auth/core 15 | 16 | or 17 | 18 | npm i @astro-auth/core 19 | ``` 20 | 21 | ## Documentation 22 | 23 | Read the documentation at 24 | 25 | ## Contributing 26 | 27 | We're open to all community contributions, just make a pull request! 28 | -------------------------------------------------------------------------------- /apps/dev/.gitignore: -------------------------------------------------------------------------------- 1 | # build output 2 | dist/ 3 | .output/ 4 | 5 | # dependencies 6 | node_modules/ 7 | 8 | # logs 9 | npm-debug.log* 10 | yarn-debug.log* 11 | yarn-error.log* 12 | pnpm-debug.log* 13 | 14 | 15 | # environment variables 16 | .env 17 | .env.production 18 | 19 | # macOS-specific files 20 | .DS_Store 21 | -------------------------------------------------------------------------------- /apps/dev/.npmrc: -------------------------------------------------------------------------------- 1 | # Expose Astro dependencies for `pnpm` users 2 | shamefully-hoist=true 3 | -------------------------------------------------------------------------------- /apps/dev/.stackblitzrc: -------------------------------------------------------------------------------- 1 | { 2 | "startCommand": "npm start", 3 | "env": { 4 | "ENABLE_CJS_IMPORTS": true 5 | } 6 | } -------------------------------------------------------------------------------- /apps/dev/.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": ["astro-build.astro-vscode"], 3 | "unwantedRecommendations": [] 4 | } 5 | -------------------------------------------------------------------------------- /apps/dev/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "command": "./node_modules/.bin/astro dev", 6 | "name": "Development server", 7 | "request": "launch", 8 | "type": "node-terminal" 9 | } 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /apps/dev/README.md: -------------------------------------------------------------------------------- 1 | # Astro Starter Kit: Minimal 2 | 3 | ``` 4 | npm init astro -- --template minimal 5 | ``` 6 | 7 | [![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/withastro/astro/tree/latest/examples/minimal) 8 | 9 | > 🧑‍🚀 **Seasoned astronaut?** Delete this file. Have fun! 10 | 11 | ## 🚀 Project Structure 12 | 13 | Inside of your Astro project, you'll see the following folders and files: 14 | 15 | ``` 16 | / 17 | ├── public/ 18 | ├── src/ 19 | │ └── pages/ 20 | │ └── index.astro 21 | └── package.json 22 | ``` 23 | 24 | Astro looks for `.astro` or `.md` files in the `src/pages/` directory. Each page is exposed as a route based on its file name. 25 | 26 | There's nothing special about `src/components/`, but that's where we like to put any Astro/React/Vue/Svelte/Preact components. 27 | 28 | Any static assets, like images, can be placed in the `public/` directory. 29 | 30 | ## 🧞 Commands 31 | 32 | All commands are run from the root of the project, from a terminal: 33 | 34 | | Command | Action | 35 | |:---------------- |:-------------------------------------------- | 36 | | `npm install` | Installs dependencies | 37 | | `npm run dev` | Starts local dev server at `localhost:3000` | 38 | | `npm run build` | Build your production site to `./dist/` | 39 | | `npm run preview` | Preview your build locally, before deploying | 40 | 41 | ## 👀 Want to learn more? 42 | 43 | Feel free to check [our documentation](https://github.com/withastro/astro) or jump into our [Discord server](https://astro.build/chat). 44 | -------------------------------------------------------------------------------- /apps/dev/astro.config.mjs: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "astro/config"; 2 | import react from "@astrojs/react"; 3 | import vercel from "@astrojs/vercel"; 4 | 5 | import { astroAuthComponents } from "@astro-auth/ui"; 6 | 7 | export default defineConfig({ 8 | integrations: [react(), astroAuthComponents()], 9 | adapter: vercel(), 10 | }); 11 | -------------------------------------------------------------------------------- /apps/dev/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "astro-auth-dev", 3 | "version": "0.0.1", 4 | "private": true, 5 | "scripts": { 6 | "dev": "astro dev --experimental-integrations", 7 | "start": "astro dev --experimental-integrations", 8 | "build": "astro build --experimental-integrations", 9 | "preview": "astro preview --experimental-integrations" 10 | }, 11 | "devDependencies": { 12 | "@astrojs/react": "^0.1.0", 13 | "@astrojs/vercel": "^0.1.1", 14 | "astro": "^1.0.0-beta.28", 15 | "react": "^18.0.0", 16 | "react-dom": "^18.0.0" 17 | }, 18 | "dependencies": { 19 | "@astro-auth/client": "1.0.3", 20 | "@astro-auth/core": "1.0.11", 21 | "@astro-auth/providers": "1.0.5", 22 | "@astro-auth/ui": "1.0.3" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /apps/dev/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astro-community/astro-auth/aa651e3d827b60ff8c880e186054c92f7c71d2bc/apps/dev/public/favicon.ico -------------------------------------------------------------------------------- /apps/dev/sandbox.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "infiniteLoopProtection": true, 3 | "hardReloadOnChange": false, 4 | "view": "browser", 5 | "template": "node", 6 | "container": { 7 | "port": 3000, 8 | "startScript": "start", 9 | "node": "14" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /apps/dev/src/components/IntroSection/index.module.css: -------------------------------------------------------------------------------- 1 | .content { 2 | height: 100vh; 3 | width: 100%; 4 | display: flex; 5 | flex-direction: column; 6 | justify-content: center; 7 | align-items: center; 8 | text-align: center; 9 | } 10 | 11 | .content h2 { 12 | line-height: 1.1; 13 | } 14 | 15 | .content .profileImage { 16 | margin-bottom: 25px; 17 | border-radius: 50%; 18 | width: 140px; 19 | aspect-ratio: 1/1; 20 | } 21 | 22 | .getStarted { 23 | margin-top: 30px; 24 | display: grid; 25 | grid-template-columns: repeat(2, 1fr); 26 | gap: 13px 10px; 27 | 28 | margin-bottom: 20px; 29 | } 30 | 31 | .getStartedLogged { 32 | display: flex; 33 | justify-content: center; 34 | align-items: center; 35 | } 36 | 37 | 38 | @media screen and (max-width: 700px) { 39 | .getStarted { 40 | grid-template-columns: repeat(1, 1fr); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /apps/dev/src/components/IntroSection/index.tsx: -------------------------------------------------------------------------------- 1 | import { FC, useRef } from "react"; 2 | import { 3 | Button, 4 | Title, 5 | GoogleButton, 6 | DiscordButton, 7 | TwitterButton, 8 | GithubButton, 9 | MetamaskButton, 10 | } from "@astro-auth/ui"; 11 | import { signIn, signOut, ReactStateStore } from "@astro-auth/client"; 12 | import styles from "./index.module.css"; 13 | 14 | interface IntroSectionProps { 15 | isSignIn?: boolean; 16 | user?: any; 17 | } 18 | 19 | const IntroSection: FC = ({ isSignIn = false, user }) => { 20 | const storedUser = ReactStateStore.useUser({ update: true }); 21 | 22 | const emailRef = useRef(null); 23 | const passwordRef = useRef(null); 24 | 25 | return ( 26 |
27 | {isSignIn ? ( 28 | <> 29 | {user.user && ( 30 | <> 31 | 32 | {`Welcome To The Dashboard ${user.user.name}`} 33 | 34 | )} 35 | 36 | ) : ( 37 | Welcome To Astro Auth Demo 38 | )} 39 | 40 | {isSignIn ? ( 41 |
42 | 43 |
44 | ) : ( 45 |
46 |
47 | 48 | 49 | 50 | 51 |
52 | 53 |
54 | 55 | 56 |
57 | 58 | 71 | 72 | 73 |
74 | )} 75 | 76 | {JSON.stringify(storedUser)} 77 |
78 | ); 79 | }; 80 | 81 | export default IntroSection; 82 | -------------------------------------------------------------------------------- /apps/dev/src/components/UserStore/index.tsx: -------------------------------------------------------------------------------- 1 | import { ReactStateStore } from "@astro-auth/client"; 2 | 3 | const UserStore = ({ user }: { user?: any }) => { 4 | return ; 5 | }; 6 | 7 | export default UserStore; 8 | -------------------------------------------------------------------------------- /apps/dev/src/env.d.ts: -------------------------------------------------------------------------------- 1 | interface ImportMetaEnv { 2 | readonly GOOGLE_CLIENT_ID: string; 3 | readonly GOOGLE_CLIENT_SECRET: string; 4 | 5 | readonly DISCORD_CLIENT_ID: string; 6 | readonly DISCORD_CLIENT_SECRET: string; 7 | 8 | readonly TWITTER_CLIENT_ID: string; 9 | readonly TWITTER_CLIENT_SECRET: string; 10 | 11 | readonly GITHUB_CLIENT_ID: string; 12 | readonly GITHUB_CLIENT_SECRET: string; 13 | } 14 | 15 | interface ImportMeta { 16 | readonly env: ImportMetaEnv; 17 | } 18 | -------------------------------------------------------------------------------- /apps/dev/src/modules.d.ts: -------------------------------------------------------------------------------- 1 | declare module "*.module.css" { 2 | const classes: { [key: string]: string }; 3 | export default classes; 4 | } 5 | -------------------------------------------------------------------------------- /apps/dev/src/pages/api/auth/[...astroauth].ts: -------------------------------------------------------------------------------- 1 | import AstroAuth from "@astro-auth/core"; 2 | import { 3 | GoogleProvider, 4 | DiscordProvider, 5 | CredentialProvider, 6 | TwitterProvider, 7 | GithubProvider, 8 | MetamaskProvider, 9 | } from "@astro-auth/providers"; 10 | 11 | export const all = AstroAuth({ 12 | authProviders: [ 13 | GoogleProvider({ 14 | clientId: import.meta.env.GOOGLE_CLIENT_ID, 15 | clientSecret: import.meta.env.GOOGLE_CLIENT_SECRET, 16 | }), 17 | DiscordProvider({ 18 | clientId: import.meta.env.DISCORD_CLIENT_ID, 19 | clientSecret: import.meta.env.DISCORD_CLIENT_SECRET, 20 | }), 21 | TwitterProvider({ 22 | clientId: import.meta.env.TWITTER_CLIENT_ID, 23 | clientSecret: import.meta.env.TWITTER_CLIENT_SECRET, 24 | scope: ["users.read", "offline.access"].join(" "), 25 | }), 26 | GithubProvider({ 27 | clientId: import.meta.env.GITHUB_CLIENT_ID, 28 | clientSecret: import.meta.env.GITHUB_CLIENT_SECRET, 29 | }), 30 | CredentialProvider({ 31 | authorize: (properties) => { 32 | if (properties.email == "osadavidath@gmail.com") { 33 | return properties; 34 | } 35 | 36 | return null; 37 | }, 38 | }), 39 | MetamaskProvider({ 40 | authorize: async (properties) => properties, 41 | signMessage: "Hello From Astro Auth", 42 | }), 43 | ], 44 | hooks: { 45 | jwt: async (user) => { 46 | if (user.provider == "metamask") { 47 | return user; 48 | } 49 | 50 | return { 51 | accessToken: user.access_token, 52 | refreshToken: user.refresh_token, 53 | user: { 54 | ...user.user, 55 | originalUser: undefined, 56 | }, 57 | }; 58 | }, 59 | signIn: async () => { 60 | return true; 61 | }, 62 | redirectError: async (error) => { 63 | console.log(error.message); 64 | return "/"; 65 | }, 66 | account: (user) => { 67 | return { 68 | ...user, 69 | isGood: Math.random() >= 0.5, 70 | }; 71 | }, 72 | }, 73 | }); 74 | -------------------------------------------------------------------------------- /apps/dev/src/pages/api/privateRoute.ts: -------------------------------------------------------------------------------- 1 | import { getUser } from "@astro-auth/core"; 2 | 3 | export const get = (_, request: Request) => { 4 | const user = getUser({ server: request }); 5 | 6 | if (!user) { 7 | return new Response( 8 | JSON.stringify({ 9 | secret: "No ACCESS ⚠️", 10 | }), 11 | { 12 | status: 500, 13 | headers: { 14 | "Content-Type": "application/json", 15 | }, 16 | } 17 | ); 18 | } 19 | 20 | return new Response( 21 | JSON.stringify({ 22 | secret: "🤫", 23 | user, 24 | }), 25 | { 26 | status: 200, 27 | headers: { 28 | "Content-Type": "application/json", 29 | }, 30 | } 31 | ); 32 | }; 33 | -------------------------------------------------------------------------------- /apps/dev/src/pages/dashboard.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import IntroSection from "../components/IntroSection"; 3 | import { getUser, redirectUser } from "@astro-auth/core"; 4 | import UserStore from "../components/UserStore"; 5 | 6 | const user = getUser({ client: Astro }); 7 | if (!user) { 8 | return redirectUser("/"); 9 | } 10 | --- 11 | 12 | 13 | 14 | 15 | 16 | Astro 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /apps/dev/src/pages/index.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import IntroSection from "../components/IntroSection"; 3 | import { getUser,getError } from "@astro-auth/core"; 4 | import UserStore from "../components/UserStore"; 5 | 6 | 7 | const user = getUser({ client:Astro }); 8 | const error = getError(Astro); 9 | --- 10 | 11 | 12 | 13 | 14 | 15 | 16 | Astro 17 | 18 | 19 | 20 |
21 | {error &&
{error}
} 22 |
23 | 24 | 25 |

Hello

26 | 27 | 28 | -------------------------------------------------------------------------------- /apps/dev/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "moduleResolution": "node", 4 | "module": "ES2020" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- 1 | # build output 2 | dist/ 3 | .output/ 4 | 5 | # dependencies 6 | node_modules/ 7 | 8 | # logs 9 | npm-debug.log* 10 | yarn-debug.log* 11 | yarn-error.log* 12 | pnpm-debug.log* 13 | 14 | 15 | # environment variables 16 | .env 17 | .env.production 18 | 19 | # macOS-specific files 20 | .DS_Store 21 | -------------------------------------------------------------------------------- /docs/.npmrc: -------------------------------------------------------------------------------- 1 | # Expose Astro dependencies for `pnpm` users 2 | shamefully-hoist=true 3 | -------------------------------------------------------------------------------- /docs/.stackblitzrc: -------------------------------------------------------------------------------- 1 | { 2 | "startCommand": "npm start", 3 | "env": { 4 | "ENABLE_CJS_IMPORTS": true 5 | } 6 | } -------------------------------------------------------------------------------- /docs/.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": ["astro-build.astro-vscode"], 3 | "unwantedRecommendations": [] 4 | } 5 | -------------------------------------------------------------------------------- /docs/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "command": "./node_modules/.bin/astro dev", 6 | "name": "Development server", 7 | "request": "launch", 8 | "type": "node-terminal" 9 | } 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /docs/astro.config.mjs: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'astro/config'; 2 | import preact from '@astrojs/preact'; 3 | import react from '@astrojs/react'; 4 | 5 | // https://astro.build/config 6 | export default defineConfig({ 7 | integrations: [ 8 | // Enable Preact to support Preact JSX components. 9 | preact(), 10 | // Enable React for the Algolia search component. 11 | react(), 12 | ], 13 | }); 14 | -------------------------------------------------------------------------------- /docs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@example/docs", 3 | "version": "0.0.1", 4 | "private": true, 5 | "scripts": { 6 | "dev": "astro dev", 7 | "start": "astro dev", 8 | "build": "astro build", 9 | "preview": "astro preview" 10 | }, 11 | "dependencies": { 12 | "@algolia/client-search": "^4.13.0", 13 | "@docsearch/css": "^3.0.0", 14 | "@docsearch/react": "^3.0.0", 15 | "@types/react": "^17.0.44", 16 | "preact": "^10.7.1", 17 | "react": "^18.0.0", 18 | "react-dom": "^18.0.0" 19 | }, 20 | "devDependencies": { 21 | "@astrojs/preact": "^0.1.1", 22 | "@astrojs/react": "^0.1.0", 23 | "astro": "^1.0.0-beta.12", 24 | "@astrojs/renderer-preact": "^0.5.0" 25 | } 26 | } -------------------------------------------------------------------------------- /docs/public/astro.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astro-community/astro-auth/aa651e3d827b60ff8c880e186054c92f7c71d2bc/docs/public/astro.png -------------------------------------------------------------------------------- /docs/public/astroauth/astroauth-tp-b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astro-community/astro-auth/aa651e3d827b60ff8c880e186054c92f7c71d2bc/docs/public/astroauth/astroauth-tp-b.png -------------------------------------------------------------------------------- /docs/public/astroauth/astroauth-tp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astro-community/astro-auth/aa651e3d827b60ff8c880e186054c92f7c71d2bc/docs/public/astroauth/astroauth-tp.png -------------------------------------------------------------------------------- /docs/public/astroauth/astroauth.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astro-community/astro-auth/aa651e3d827b60ff8c880e186054c92f7c71d2bc/docs/public/astroauth/astroauth.png -------------------------------------------------------------------------------- /docs/public/make-scrollable-code-focusable.js: -------------------------------------------------------------------------------- 1 | Array.from(document.getElementsByTagName('pre')).forEach((element) => { 2 | element.setAttribute('tabindex', '0'); 3 | }); 4 | -------------------------------------------------------------------------------- /docs/sandbox.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "infiniteLoopProtection": true, 3 | "hardReloadOnChange": false, 4 | "view": "browser", 5 | "template": "node", 6 | "container": { 7 | "port": 3000, 8 | "startScript": "start", 9 | "node": "14" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /docs/src/components/Footer/AvatarList.astro: -------------------------------------------------------------------------------- 1 | --- 2 | // fetch all commits for just this page's path 3 | const path = 'docs/' + Astro.props.path; 4 | const url = `https://api.github.com/repos/withastro/astro/commits?path=${path}`; 5 | const commitsURL = `https://github.com/withastro/astro/commits/main/${path}`; 6 | 7 | async function getCommits(url) { 8 | try { 9 | const token = import.meta.env.SNOWPACK_PUBLIC_GITHUB_TOKEN; 10 | if (!token) { 11 | throw new Error('Cannot find "SNOWPACK_PUBLIC_GITHUB_TOKEN" used for escaping rate-limiting.'); 12 | } 13 | 14 | const auth = `Basic ${Buffer.from(token, 'binary').toString('base64')}`; 15 | 16 | const res = await fetch(url, { 17 | method: 'GET', 18 | headers: { 19 | Authorization: auth, 20 | 'User-Agent': 'astro-docs/1.0', 21 | }, 22 | }); 23 | 24 | const data = await res.json(); 25 | 26 | if (!res.ok) { 27 | throw new Error( 28 | `Request to fetch commits failed. Reason: ${res.statusText} 29 | Message: ${data.message}` 30 | ); 31 | } 32 | 33 | return data; 34 | } catch (e) { 35 | console.warn(`[error] /src/components/AvatarList.astro 36 | ${e?.message ?? e}`); 37 | return new Array(); 38 | } 39 | } 40 | 41 | function removeDups(arr) { 42 | if (!arr) { 43 | return new Array(); 44 | } 45 | let map = new Map(); 46 | 47 | for (let item of arr) { 48 | let author = item.author; 49 | // Deduplicate based on author.id 50 | map.set(author.id, { login: author.login, id: author.id }); 51 | } 52 | 53 | return Array.from(map.values()); 54 | } 55 | 56 | const data = await getCommits(url); 57 | const unique = removeDups(data); 58 | const recentContributors = unique.slice(0, 3); // only show avatars for the 3 most recent contributors 59 | const additionalContributors = unique.length - recentContributors.length; // list the rest of them as # of extra contributors 60 | --- 61 | 62 | 63 |
64 |
    65 | {recentContributors.map((item) => ( 66 |
  • 67 | 68 | {`Contributor 69 | 70 |
  • 71 | ))} 72 |
73 | {additionalContributors > 0 && ( 74 | 75 | {`and ${additionalContributors} additional contributor${additionalContributors > 1 ? 's' : ''}.`} 76 | 77 | )} 78 | {unique.length === 0 && Contributors} 79 |
80 | 81 | 150 | -------------------------------------------------------------------------------- /docs/src/components/Footer/Footer.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import AvatarList from './AvatarList.astro'; 3 | const { path } = Astro.props; 4 | --- 5 | 6 |
7 | 8 |
9 | 10 | 17 | -------------------------------------------------------------------------------- /docs/src/components/HeadCommon.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import "../styles/theme.css"; 3 | import "../styles/index.css"; 4 | --- 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 21 | 22 | 23 | 24 | 25 | 26 | 30 | 31 | 32 | 39 | -------------------------------------------------------------------------------- /docs/src/components/HeadSEO.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import { SITE, OPEN_GRAPH } from '../config.ts'; 3 | export interface Props { 4 | content: any; 5 | site: any; 6 | canonicalURL: URL | string; 7 | } 8 | const { content = {}, canonicalURL } = Astro.props; 9 | const formattedContentTitle = content.title ? `${content.title} 🚀 ${SITE.title}` : SITE.title; 10 | const imageSrc = content?.image?.src ?? OPEN_GRAPH.image.src; 11 | const canonicalImageSrc = new URL(imageSrc, Astro.site); 12 | const imageAlt = content?.image?.alt ?? OPEN_GRAPH.image.alt; 13 | --- 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 42 | -------------------------------------------------------------------------------- /docs/src/components/Header/Header.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import { getLanguageFromURL, KNOWN_LANGUAGE_CODES } from '../../languages.ts'; 3 | import * as CONFIG from '../../config.ts'; 4 | import SkipToContent from './SkipToContent.astro'; 5 | import SidebarToggle from './SidebarToggle.tsx'; 6 | import LanguageSelect from './LanguageSelect.tsx'; 7 | import Search from './Search.tsx'; 8 | 9 | const { currentPage } = Astro.props; 10 | const lang = currentPage && getLanguageFromURL(currentPage); 11 | --- 12 | 13 |
14 | 15 | 30 |
31 | 32 | 143 | -------------------------------------------------------------------------------- /docs/src/components/Header/LanguageSelect.css: -------------------------------------------------------------------------------- 1 | .language-select { 2 | flex-grow: 1; 3 | width: 48px; 4 | box-sizing: border-box; 5 | margin: 0; 6 | padding: 0.33em 0.5em; 7 | overflow: visible; 8 | font-weight: 500; 9 | font-size: 1rem; 10 | font-family: inherit; 11 | line-height: inherit; 12 | background-color: var(--theme-bg); 13 | border-color: var(--theme-text-lighter); 14 | color: var(--theme-text-light); 15 | border-style: solid; 16 | border-width: 1px; 17 | border-radius: 0.25rem; 18 | outline: 0; 19 | cursor: pointer; 20 | transition-timing-function: ease-out; 21 | transition-duration: 0.2s; 22 | transition-property: border-color, color; 23 | -webkit-font-smoothing: antialiased; 24 | padding-left: 30px; 25 | padding-right: 1rem; 26 | } 27 | .language-select-wrapper .language-select:hover, 28 | .language-select-wrapper .language-select:focus { 29 | color: var(--theme-text); 30 | border-color: var(--theme-text-light); 31 | } 32 | .language-select-wrapper { 33 | color: var(--theme-text-light); 34 | position: relative; 35 | } 36 | .language-select-wrapper > svg { 37 | position: absolute; 38 | top: 7px; 39 | left: 10px; 40 | pointer-events: none; 41 | } 42 | 43 | @media (min-width: 50em) { 44 | .language-select { 45 | width: 100%; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /docs/src/components/Header/LanguageSelect.tsx: -------------------------------------------------------------------------------- 1 | import type { FunctionalComponent } from 'preact'; 2 | import { h } from 'preact'; 3 | import './LanguageSelect.css'; 4 | import { KNOWN_LANGUAGES, langPathRegex } from '../../languages'; 5 | 6 | const LanguageSelect: FunctionalComponent<{ lang: string }> = ({ lang }) => { 7 | return ( 8 |
9 | 27 | 45 |
46 | ); 47 | }; 48 | 49 | export default LanguageSelect; 50 | -------------------------------------------------------------------------------- /docs/src/components/Header/Search.css: -------------------------------------------------------------------------------- 1 | /** Style Algolia */ 2 | :root { 3 | --docsearch-primary-color: var(--theme-accent); 4 | --docsearch-logo-color: var(--theme-text); 5 | } 6 | .search-input { 7 | flex-grow: 1; 8 | box-sizing: border-box; 9 | width: 100%; 10 | margin: 0; 11 | padding: 0.33em 0.5em; 12 | overflow: visible; 13 | font-weight: 500; 14 | font-size: 1rem; 15 | font-family: inherit; 16 | line-height: inherit; 17 | background-color: var(--theme-divider); 18 | border-color: var(--theme-divider); 19 | color: var(--theme-text-light); 20 | border-style: solid; 21 | border-width: 1px; 22 | border-radius: 0.25rem; 23 | outline: 0; 24 | cursor: pointer; 25 | transition-timing-function: ease-out; 26 | transition-duration: 0.2s; 27 | transition-property: border-color, color; 28 | -webkit-font-smoothing: antialiased; 29 | } 30 | .search-input:hover, 31 | .search-input:focus { 32 | color: var(--theme-text); 33 | border-color: var(--theme-text-light); 34 | } 35 | .search-input:hover::placeholder, 36 | .search-input:focus::placeholder { 37 | color: var(--theme-text-light); 38 | } 39 | .search-input::placeholder { 40 | color: var(--theme-text-light); 41 | } 42 | .search-hint { 43 | position: absolute; 44 | top: 7px; 45 | right: 19px; 46 | padding: 3px 5px; 47 | display: none; 48 | display: none; 49 | align-items: center; 50 | justify-content: center; 51 | letter-spacing: 0.125em; 52 | font-size: 13px; 53 | font-family: var(--font-mono); 54 | pointer-events: none; 55 | border-color: var(--theme-text-lighter); 56 | color: var(--theme-text-light); 57 | border-style: solid; 58 | border-width: 1px; 59 | border-radius: 0.25rem; 60 | line-height: 14px; 61 | } 62 | 63 | @media (min-width: 50em) { 64 | .search-hint { 65 | display: flex; 66 | } 67 | } 68 | 69 | /* ------------------------------------------------------------ *\ 70 | DocSearch (Algolia) 71 | \* ------------------------------------------------------------ */ 72 | 73 | .DocSearch-Modal .DocSearch-Hit a { 74 | box-shadow: none; 75 | border: 1px solid var(--theme-accent); 76 | } 77 | -------------------------------------------------------------------------------- /docs/src/components/Header/Search.tsx: -------------------------------------------------------------------------------- 1 | /* jsxImportSource: react */ 2 | import { useState, useCallback, useRef } from 'react'; 3 | import { createPortal } from 'react-dom'; 4 | import * as docSearchReact from '@docsearch/react'; 5 | import * as CONFIG from '../../config'; 6 | import '@docsearch/css/dist/style.css'; 7 | import './Search.css'; 8 | 9 | const { DocSearchModal, useDocSearchKeyboardEvents } = docSearchReact.default; 10 | 11 | export default function Search() { 12 | const [isOpen, setIsOpen] = useState(false); 13 | const searchButtonRef = useRef(); 14 | const [initialQuery, setInitialQuery] = useState(null); 15 | 16 | const onOpen = useCallback(() => { 17 | setIsOpen(true); 18 | }, [setIsOpen]); 19 | 20 | const onClose = useCallback(() => { 21 | setIsOpen(false); 22 | }, [setIsOpen]); 23 | 24 | const onInput = useCallback( 25 | (e) => { 26 | setIsOpen(true); 27 | setInitialQuery(e.key); 28 | }, 29 | [setIsOpen, setInitialQuery] 30 | ); 31 | 32 | useDocSearchKeyboardEvents({ 33 | isOpen, 34 | onOpen, 35 | onClose, 36 | onInput, 37 | searchButtonRef, 38 | }); 39 | 40 | return ( 41 | <> 42 | 59 | {isOpen && 60 | createPortal( 61 | { 69 | return items.map((item) => { 70 | // We transform the absolute URL into a relative URL to 71 | // work better on localhost, preview URLS. 72 | const a = document.createElement('a'); 73 | a.href = item.url; 74 | const hash = a.hash === '#overview' ? '' : a.hash; 75 | return { 76 | ...item, 77 | url: `${a.pathname}${hash}`, 78 | }; 79 | }); 80 | }} 81 | />, 82 | document.body 83 | )} 84 | 85 | ); 86 | } 87 | -------------------------------------------------------------------------------- /docs/src/components/Header/SidebarToggle.tsx: -------------------------------------------------------------------------------- 1 | import type { FunctionalComponent } from 'preact'; 2 | import { h, Fragment } from 'preact'; 3 | import { useState, useEffect } from 'preact/hooks'; 4 | 5 | const MenuToggle: FunctionalComponent = () => { 6 | const [sidebarShown, setSidebarShown] = useState(false); 7 | 8 | useEffect(() => { 9 | const body = document.getElementsByTagName('body')[0]; 10 | if (sidebarShown) { 11 | body.classList.add('mobile-sidebar-toggle'); 12 | } else { 13 | body.classList.remove('mobile-sidebar-toggle'); 14 | } 15 | }, [sidebarShown]); 16 | 17 | return ( 18 | 41 | ); 42 | }; 43 | 44 | export default MenuToggle; 45 | -------------------------------------------------------------------------------- /docs/src/components/Header/SkipToContent.astro: -------------------------------------------------------------------------------- 1 | 2 | 3 | 23 | -------------------------------------------------------------------------------- /docs/src/components/LeftSidebar/LeftSidebar.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import { getLanguageFromURL } from '../../languages.ts'; 3 | import { SIDEBAR } from '../../config.ts'; 4 | const { currentPage } = Astro.props; 5 | const currentPageMatch = currentPage.slice(1); 6 | const langCode = getLanguageFromURL(currentPage); 7 | // SIDEBAR is a flat array. Group it by sections to properly render. 8 | const sidebarSections = SIDEBAR[langCode].reduce((col, item, i) => { 9 | // If the first item is not a section header, create a new container section. 10 | if (i === 0) { 11 | if (!item.header) { 12 | const pesudoSection = { text: "" }; 13 | col.push({ ...pesudoSection, children: [] }); 14 | } 15 | } 16 | if (item.header) { 17 | col.push({ ...item, children: [] }); 18 | } else { 19 | col[col.length - 1].children.push(item); 20 | } 21 | return col; 22 | }, []); 23 | --- 24 | 25 | 45 | 46 | 54 | 55 | 120 | -------------------------------------------------------------------------------- /docs/src/components/PageContent/PageContent.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import MoreMenu from '../RightSidebar/MoreMenu.astro'; 3 | import TableOfContents from '../RightSidebar/TableOfContents.tsx'; 4 | 5 | const { content, githubEditUrl } = Astro.props; 6 | const title = content.title; 7 | const headers = content.astro.headers; 8 | --- 9 | 10 |
11 |
12 |

{title}

13 | 16 | 17 |
18 | 21 |
22 | 23 | 45 | -------------------------------------------------------------------------------- /docs/src/components/RightSidebar/MoreMenu.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import ThemeToggleButton from './ThemeToggleButton.tsx'; 3 | 4 | const { editHref } = Astro.props; 5 | --- 6 | 7 | 8 | 35 |
36 | 37 |
38 | 39 | 47 | -------------------------------------------------------------------------------- /docs/src/components/RightSidebar/RightSidebar.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import TableOfContents from './TableOfContents.tsx'; 3 | import MoreMenu from './MoreMenu.astro'; 4 | const { content, githubEditUrl } = Astro.props; 5 | const headers = content.astro.headers; 6 | --- 7 | 8 | 14 | 15 | 28 | -------------------------------------------------------------------------------- /docs/src/components/RightSidebar/TableOfContents.tsx: -------------------------------------------------------------------------------- 1 | import type { FunctionalComponent } from "preact"; 2 | import { h, Fragment } from "preact"; 3 | import { useState, useEffect, useRef } from "preact/hooks"; 4 | 5 | const TableOfContents: FunctionalComponent<{ headers: any[] }> = ({ 6 | headers = [], 7 | }) => { 8 | const itemOffsets = useRef([]); 9 | const [activeId, setActiveId] = useState(undefined); 10 | 11 | useEffect(() => { 12 | const getItemOffsets = () => { 13 | const titles = document.querySelectorAll("article :is(h1, h2, h3, h4)"); 14 | itemOffsets.current = Array.from(titles).map((title) => ({ 15 | id: title.id, 16 | topOffset: title.getBoundingClientRect().top + window.scrollY, 17 | })); 18 | }; 19 | 20 | getItemOffsets(); 21 | window.addEventListener("resize", getItemOffsets); 22 | 23 | return () => { 24 | window.removeEventListener("resize", getItemOffsets); 25 | }; 26 | }, []); 27 | 28 | return ( 29 | <> 30 |

On this page

31 |
    32 | {headers 33 | .filter(({ depth }) => depth > 1 && depth < 4) 34 | .map((header) => ( 35 |
  • 40 | {header.text} 41 |
  • 42 | ))} 43 |
44 | 45 | ); 46 | }; 47 | 48 | export default TableOfContents; 49 | -------------------------------------------------------------------------------- /docs/src/components/RightSidebar/ThemeToggleButton.css: -------------------------------------------------------------------------------- 1 | .theme-toggle { 2 | display: inline-flex; 3 | align-items: center; 4 | gap: 0.25em; 5 | padding: 0.33em 0.67em; 6 | border-radius: 99em; 7 | background-color: var(--theme-code-inline-bg); 8 | } 9 | 10 | .theme-toggle > label:focus-within { 11 | outline: 2px solid transparent; 12 | box-shadow: 0 0 0 0.08em var(--theme-accent), 0 0 0 0.12em white; 13 | } 14 | 15 | .theme-toggle > label { 16 | color: var(--theme-code-inline-text); 17 | position: relative; 18 | display: flex; 19 | align-items: center; 20 | justify-content: center; 21 | opacity: 0.5; 22 | } 23 | 24 | .theme-toggle .checked { 25 | color: var(--theme-accent); 26 | opacity: 1; 27 | } 28 | 29 | input[name='theme-toggle'] { 30 | position: absolute; 31 | opacity: 0; 32 | top: 0; 33 | right: 0; 34 | bottom: 0; 35 | left: 0; 36 | z-index: -1; 37 | } 38 | -------------------------------------------------------------------------------- /docs/src/components/RightSidebar/ThemeToggleButton.tsx: -------------------------------------------------------------------------------- 1 | import type { FunctionalComponent } from "preact"; 2 | import { h, Fragment } from "preact"; 3 | import { useState, useEffect } from "preact/hooks"; 4 | import "./ThemeToggleButton.css"; 5 | 6 | const themes = ["light", "dark"]; 7 | 8 | const icons = [ 9 | 16 | 21 | , 22 | 29 | 30 | , 31 | ]; 32 | 33 | const ThemeToggle: FunctionalComponent = () => { 34 | const [theme, setTheme] = useState(() => { 35 | if (import.meta.env.SSR) { 36 | return undefined; 37 | } 38 | if (typeof localStorage !== "undefined" && localStorage.getItem("theme")) { 39 | return localStorage.getItem("theme"); 40 | } 41 | if (window.matchMedia("(prefers-color-scheme: dark)").matches) { 42 | return "dark"; 43 | } 44 | return "light"; 45 | }); 46 | 47 | useEffect(() => { 48 | const root = document.documentElement; 49 | if (theme === "light") { 50 | root.classList.remove("theme-dark"); 51 | } else { 52 | root.classList.add("theme-dark"); 53 | } 54 | }, [theme]); 55 | 56 | return ( 57 |
58 | {themes.map((t, i) => { 59 | const icon = icons[i]; 60 | const checked = t === theme; 61 | return ( 62 | 77 | ); 78 | })} 79 |
80 | ); 81 | }; 82 | 83 | export default ThemeToggle; 84 | -------------------------------------------------------------------------------- /docs/src/config.ts: -------------------------------------------------------------------------------- 1 | export const SITE = { 2 | title: "Astro Auth", 3 | description: "The One Stop Solution To Authentication With Astro", 4 | defaultLanguage: "en_US", 5 | }; 6 | 7 | export const OPEN_GRAPH = { 8 | image: { 9 | src: "https://raw.githubusercontent.com/osadavc/astro-auth/master/docs/public/astroauth_bg.png?token=GHSAT0AAAAAABLHC2RY3WJCGLNM7UTPMA2CYTDVLPA", 10 | alt: "The One Stop Solution To Authentication With Astro", 11 | }, 12 | twitter: "osadavc", 13 | }; 14 | 15 | export const KNOWN_LANGUAGES = { 16 | English: "en", 17 | }; 18 | 19 | export const GITHUB_EDIT_URL = 20 | "https://github.com/osadavc/astro-auth/tree/master/docs/"; 21 | 22 | // Uncomment this to add an "Join our Community" button to every page of documentation. 23 | // export const COMMUNITY_INVITE_URL = `https://astro.build/chat`; 24 | 25 | // Uncomment this to enable site search. 26 | // See "Algolia" section of the README for more information. 27 | // export const ALGOLIA = { 28 | // indexName: 'XXXXXXXXXX', 29 | // appId: 'XXXXXXXXXX', 30 | // apiKey: 'XXXXXXXXXX', 31 | // } 32 | 33 | export const SIDEBAR = { 34 | en: [ 35 | { text: "Getting Started", header: true }, 36 | { text: "Introduction", link: "getting-started/introduction" }, 37 | { text: "Getting Started", link: "getting-started/getting-started" }, 38 | { 39 | text: "Using Astro Auth", 40 | link: "getting-started/using-astro-auth", 41 | }, 42 | { text: "Astro Auth CLI", link: "getting-started/astro-auth-cli" }, 43 | { text: "Typescript Support", link: "getting-started/typescript-support" }, 44 | 45 | { text: "Core", header: true }, 46 | { text: "Get User", link: "core/get-user" }, 47 | { text: "Redirect User", link: "core/redirect-user" }, 48 | { text: "Get Error", link: "core/get-error" }, 49 | { text: "Hooks", link: "hooks/jwt" }, 50 | 51 | { text: "Hooks", header: true }, 52 | { text: "JWT Hook", link: "hooks/jwt" }, 53 | { text: "Sign In Hook", link: "hooks/sign-in" }, 54 | { text: "Account Hook", link: "hooks/account" }, 55 | { text: "Redirect Error Hook", link: "hooks/redirectError" }, 56 | 57 | { text: "UI", header: true }, 58 | { text: "Instructions", link: "ui-library/instructions" }, 59 | { text: "Button", link: "ui-library/button" }, 60 | { text: "Title", link: "ui-library/title" }, 61 | { text: "Provider Buttons", link: "ui-library/provider-buttons" }, 62 | 63 | { text: "Client", header: true }, 64 | { text: "Sign In", link: "client/signIn" }, 65 | { text: "Sign Out", link: "client/signOut" }, 66 | { text: "State Store", link: "state-store/react" }, 67 | 68 | { text: "State Store", header: true }, 69 | { text: "React", link: "state-store/react" }, 70 | { text: "Other Frameworks", link: "state-store/other-frameworks" }, 71 | 72 | { text: "Providers", header: true }, 73 | { text: "OAuth Provider", link: "providers/oauth" }, 74 | { text: "Credentials Provider", link: "providers/credentials" }, 75 | { text: "Metamask Provider", link: "providers/metamask" }, 76 | 77 | { text: "Extra", header: true }, 78 | { text: "Database Integration", link: "extra/database-integration" }, 79 | ], 80 | }; 81 | -------------------------------------------------------------------------------- /docs/src/env.d.ts: -------------------------------------------------------------------------------- 1 | interface ImportMetaEnv { 2 | readonly SSR: boolean; 3 | } 4 | 5 | interface ImportMeta { 6 | readonly env: ImportMetaEnv; 7 | } 8 | -------------------------------------------------------------------------------- /docs/src/languages.ts: -------------------------------------------------------------------------------- 1 | import { KNOWN_LANGUAGES } from './config'; 2 | 3 | export { KNOWN_LANGUAGES }; 4 | export const KNOWN_LANGUAGE_CODES = Object.values(KNOWN_LANGUAGES); 5 | export const langPathRegex = /\/([a-z]{2}-?[A-Z]{0,2})\//; 6 | 7 | export function getLanguageFromURL(pathname: string) { 8 | const langCodeMatch = pathname.match(langPathRegex); 9 | return langCodeMatch ? langCodeMatch[1] : 'en'; 10 | } 11 | -------------------------------------------------------------------------------- /docs/src/layouts/MainLayout.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import HeadCommon from "../components/HeadCommon.astro"; 3 | import HeadSEO from "../components/HeadSEO.astro"; 4 | import Header from "../components/Header/Header.astro"; 5 | import Footer from "../components/Footer/Footer.astro"; 6 | import PageContent from "../components/PageContent/PageContent.astro"; 7 | import LeftSidebar from "../components/LeftSidebar/LeftSidebar.astro"; 8 | import RightSidebar from "../components/RightSidebar/RightSidebar.astro"; 9 | import * as CONFIG from "../config"; 10 | 11 | const { content = {} } = Astro.props; 12 | const currentPage = new URL(Astro.request.url).pathname; 13 | const currentFile = `src/pages${currentPage.replace(/\/$/, "")}.md`; 14 | const githubEditUrl = 15 | CONFIG.GITHUB_EDIT_URL && CONFIG.GITHUB_EDIT_URL + currentFile; 16 | --- 17 | 18 | 19 | 20 | 21 | 22 | {content.title 24 | ? `${content.title} 🚀 ${CONFIG.SITE.title}` 25 | : CONFIG.SITE.title} 27 | 28 | 110 | 111 | 112 | 113 |
114 |
115 | 118 |
119 | 120 | 121 | 122 |
123 | 126 |
127 | 128 | 129 | -------------------------------------------------------------------------------- /docs/src/pages/client/signIn.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Client Sign In 3 | description: Learn About Client Sign In 4 | layout: ../../layouts/MainLayout.astro 5 | --- 6 | 7 | ## Instructions 8 | 9 | `signIn` is a function of `@astro-auth/client` that will allow you to sign in a user from the frontend. Preferably, you should use this function inside a component island. 10 | 11 | `signIn` takes one parameter which is a object that contains, `callbackURL`, `provider` and `login` properties. `callbackURL` is the route that you want to redirect the user to after signing in. `provider` is the provider that you want to use to sign in.`login` is a property that is only used when you're using the **Credentials** provider. It is the login details that you need to send over to the backend for authentications. 12 | 13 | ## Example 14 | 15 | ```js 16 | import { signIn } from "@astro-auth/client"; 17 | 18 | const Login = () => { 19 | return ( 20 |
21 | 31 |
32 | ); 33 | }; 34 | 35 | export default Login; 36 | ``` 37 | 38 | In above example, we're using the **Discord** provider to sign in. If the login is successful the user will be redirected to the `/dashboard` route. 39 | 40 | > Read more about the `login` property in the **Credentials** provider. 41 | -------------------------------------------------------------------------------- /docs/src/pages/client/signOut.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Client Sign Out 3 | description: Learn About Client Sign In 4 | layout: ../../layouts/MainLayout.astro 5 | --- 6 | 7 | ## Instructions 8 | 9 | `signOut` is a function of `@astro-auth/client` that will allow you to sign in a user from the frontend. Preferably, you should use this function inside a component island. 10 | 11 | `signOut` takes one parameter which is the callbackURL. The callbackURL is the route that you want to redirect the user to after a successful signOut signing out. 12 | 13 | ## Example 14 | 15 | ```js 16 | import { signOut } from "@astro-auth/client"; 17 | 18 | const Login = () => { 19 | return ( 20 |
21 | 28 |
29 | ); 30 | }; 31 | 32 | export default Login; 33 | ``` 34 | 35 | In above example, when user is successfully signed out, the user will be redirected to the `/` route. 36 | -------------------------------------------------------------------------------- /docs/src/pages/client/state-store.astro: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/src/pages/core/get-error.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Get Error 3 | description: Get Any Error From Server 4 | layout: ../../layouts/MainLayout.astro 5 | --- 6 | 7 | ## Instructions 8 | 9 | When a error is happened in the backend server in a user action, backend redirects the user to the a specific page with the error message. `getError` function can be used to fetch these errors and maybe display them to the user. 10 | 11 | ```js 12 | import { getError } from "@astro-auth/core"; 13 | 14 | const error = getError(Astro); 15 | ``` 16 | 17 | In above snippet the error object will be the error if it exists. If it doesn't it'll be null. 18 | -------------------------------------------------------------------------------- /docs/src/pages/core/get-user.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Get User 3 | description: Learn About Get User Function 4 | layout: ../../layouts/MainLayout.astro 5 | --- 6 | 7 | ## Introduction 8 | 9 | `getUser` is a function in the `@astro-auth/core` library that returns the user that was serialized to JWT in the `jwt` hook if a user is currently logged in. You can use the `getUser` function in your Astro frontmatter or in a API route. 10 | 11 | ### Calling `getUser` in frontmatter. 12 | 13 | Make sure to pass the `Astro` global as the client property to the function if you're using this in the frontmatter. 14 | 15 | ```js 16 | --- 17 | import { getUser } from "@astro-auth/core"; 18 | 19 | const user = getUser({ client: Astro }); 20 | --- 21 | 22 | ... 23 | ``` 24 | 25 | ### Calling `getUser` in an API route. 26 | 27 | Make sure to pass the `request` object as the server property to the function if you're using this in the frontmatter. 28 | 29 | ```js 30 | import { getUser } from "@astro-auth/core"; 31 | 32 | export const all = (_, request: Request) => { 33 | const user = getUser({ server: request }); 34 | }; 35 | ``` 36 | 37 | Once you got the user, you can act according to the returned user. `null` will be returned if there is no user logged in. 38 | -------------------------------------------------------------------------------- /docs/src/pages/core/redirect-user.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Redirect User 3 | description: Redirect User Based On User's Auth Status 4 | layout: ../../layouts/MainLayout.astro 5 | --- 6 | 7 | ## Instructions 8 | 9 | Normally in `astro`, you can use `Astro.redirect` function to redirect user based on their authentication status. But in my case I found it was not working as expected. If that is the case for you, you can use the `redirectUser` function. 10 | 11 | ```js 12 | import { getUser, redirectUser } from "@astro-auth/core"; 13 | 14 | const user = getUser({ client: Astro }); 15 | if (!user) { 16 | return redirectUser("/"); 17 | } 18 | ``` 19 | -------------------------------------------------------------------------------- /docs/src/pages/extra/database-integration.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Database Integration 3 | description: Learn How To Integrate Astro Auth With Your Favorite Database 4 | layout: ../../layouts/MainLayout.astro 5 | --- 6 | 7 | ## Instructions 8 | 9 | In some scenarios, you will need to integrate Astro Auth with a database. That might be to store additional information about the user or store some specific information about the user. 10 | 11 | Currently Astro Auth doesn't have a out of the box solution for storing user information in a database. But you can use the [`signIn`](/hooks/sign-in) hook to store the user information in a database. 12 | 13 | ## Example 14 | 15 | ```ts 16 | import AstroAuth from "@astro-auth/core"; 17 | import { GoogleProvider } from "@astro-auth/providers"; 18 | 19 | export const all = AstroAuth({ 20 | authProviders: [ 21 | GoogleProvider({ 22 | clientId: import.meta.env.GOOGLE_CLIENT_ID, 23 | clientSecret: import.meta.env.GOOGLE_CLIENT_SECRET, 24 | }), 25 | ], 26 | hooks: { 27 | // signIn hook has a argument with all the user info 28 | signIn: async (user) => { 29 | // Store the user info in a database using the user object that is returned 30 | // .... 31 | // And return true if it succeeds 32 | return true; 33 | }, 34 | }, 35 | }); 36 | ``` 37 | 38 | Here you can see the user object passed to the `signIn` hook can be used to store the user info in a database and return true if it succeeds. 39 | 40 | > Out of the box database support will be added to astro-auth soon but I think this way is even neater and more flexible. 41 | -------------------------------------------------------------------------------- /docs/src/pages/getting-started/astro-auth-cli.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Astro Auth CLI 3 | description: Using Astro Auth CLI to Easily Setup Astro Auth 4 | layout: ../../layouts/MainLayout.astro 5 | --- 6 | 7 | Normally, you would have to setup Astro Auth manually by creating a configuration file, state stores yourself. But, if you are using the CLI tool, you can use the following commands to setup Astro Auth. 8 | 9 | ### Generating A Configuration File 10 | 11 | ```bash 12 | npx @astro-auth/cli 13 | ``` 14 | 15 | Using the above command, you can generate a Astro Auth configuration file in your `api/auth` folder. If your project have a `tsconfig.json` file, this new config file will be a typescript file, if not it will be a javascript file. 16 | 17 | ### Generating A State Store 18 | 19 | > If you want to learn more about state stores, look at [this](/state-store/react) section. 20 | 21 | ```bash 22 | npx @astro-auth/cli -state 23 | ``` 24 | 25 | This will create the component that you will need to initialize your state store. 26 | -------------------------------------------------------------------------------- /docs/src/pages/getting-started/getting-started.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Getting Started 3 | description: Getting Started With Astro Auth 4 | layout: ../../layouts/MainLayout.astro 5 | --- 6 | 7 | > Astro Auth Is A Tool Kit That Consist Of 6 Packages. Two Are Mandatory While Others Are Optional and Can Be Used Or Not Based On Your Requirements. 8 | 9 | ## Basics 10 | 11 | Astro Auth consists of 5 main packages namely, 12 | 13 | 1. `@astro-auth/core` - [NPM Package](https://www.npmjs.com/package/@astro-auth/core) - This package is the core of Astro Auth. It contains all the logic and logic related to Astro Auth. 14 | 15 | 2. `@astro-auth/client` - [NPM Package](https://www.npmjs.com/package/@astro-auth/client) - This package contains all the utility functions that are used from the front matter or front end to interact with Astro Auth easily. 16 | 17 | 3. `@astro-auth/ui` (In Beta) - [NPM Package](https://www.npmjs.com/package/@astro-auth/ui) - This package contains the Astro Themed UI Elements that you can use to easily get started with Astro Auth. 18 | 19 | 4. `@astro-auth/providers` - [NPM Package](https://www.npmjs.com/package/@astro-auth/providers) - This package contains all the prebuilt auth providers that comes out of the box with Astro Auth. 20 | 21 | 5. `@astro-auth/cli` - [NPM Package](https://www.npmjs.com/package/@astro-auth/cli) - This package contains the CLI tool that you can use to get started with Astro Auth in a matter of seconds. 22 | 23 | `@astro-auth/core` and `@astro-auth/client` packages are the minimum that you require to get started with Astro Auth. Note that you can only use `@astro-auth/core` without `@astro-auth/client` but it is not the recommended way to use Astro Auth. 24 | 25 | ## Getting Started 26 | 27 | ### New Project 28 | 29 | This is the easiest way to get started. You can simply clone the example Astro Auth repository to get started. (It is highly suggested to NOT use `apps/dev` as an example because that is a development environment for the actual package) 30 | 31 | ```bash 32 | git clone https://github.com/osadavc/astro-auth-example.git 33 | ``` 34 | 35 | ### Existing Project 36 | 37 | You can either setup using the **CLI tool** or you can **setup manually**. 38 | 39 | #### Setup With CLI Tool 40 | 41 | Read the [CLI Tool](/getting-started/astro-auth-cli) section to know how to setup Astro Auth using the CLI tool. 42 | 43 | #### Manual Setup 44 | 45 | 1. Install the package using the following command. 46 | 47 | ```bash 48 | npm install @astro-auth/core 49 | ``` 50 | 51 | or 52 | 53 | ```bash 54 | yarn add @astro-auth/core 55 | ``` 56 | 57 | 2. Create API route to handle authentication 58 | 59 | Create `[...astroauth].js` inside `src/pages/api/auth` directory and add the following starter code 60 | 61 | ```js 62 | import AstroAuth from "@astro-auth/core"; 63 | 64 | export const all = AstroAuth({ 65 | authProviders: [], 66 | hooks: {}, 67 | }); 68 | ``` 69 | 70 | 3. For Astro Auth to work, you need to create to environment variables. Create a new `.env file in the root directory of your project and add the following lines. 71 | 72 | ``` 73 | ASTROAUTH_URL=YOUR_BASE_URL_HERE 74 | ASTROAUTH_SECRET=YOUR_JWT_SECRET_HERE 75 | ``` 76 | 77 | Make sure to replace the `YOUR_BASE_URL_HERE` with the URL that your applications is running on. For example, if your application is running on `http://localhost:3000`, then the `ASTROAUTH_URL` should be `http://localhost:3000`. 78 | 79 | Replace the `YOUR_JWT_SECRET_HERE` with a secret that you can use to sign the JWT token. You can use any string that you want. If you want to generate a random string, you can use the following command. 80 | 81 | ```bash 82 | openssl rand -base64 32 83 | ``` 84 | 85 | or go to this URL and generate a random one. [Generate Secret](https://generate-secret.vercel.app/32) 86 | 87 | 4. Install a SSR adapter for Astro Auth to properly work. 88 | 89 | You have to install a Astro SSR adapter for Astro Auth to work because Astro Auth relies on SSR capabilities. You can read more about that [here](https://docs.astro.build/en/guides/server-side-rendering/). 90 | 91 | 🎉 You're all set to start using Astro Auth. To learn how to use Astro Auth to authenticate users in your project, read the [Using Astro Auth](using-astro-auth) section. 92 | -------------------------------------------------------------------------------- /docs/src/pages/getting-started/introduction.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Introduction 3 | description: Introduction To Astro Auth 4 | layout: ../../layouts/MainLayout.astro 5 | --- 6 | 7 |

8 | 9 |

10 | 11 | ## About Astro Auth 🧑🏻‍🚀 12 | 13 | Astro Auth Is A Feature Complete, Developer Friendly Authentication Library For Astro. It includes a UI library with many simple components to bootstrap your app and you also have a handy CLI tool to setup a project with Astro Auth really fast. 14 | 15 | #### What Astro Auth Can Do 16 | 17 | - Supports many popular OAuth providers. 18 | - Can change or modify the shape of your JWT tokens easily. 19 | - Add any custom OAuth provider that you want to use. 20 | - Customize the default behaviour of Astro Auth via inbuilt hooks. 21 | - Use Credential provider to do ANY kind of authentication. 22 | - Get started in seconds with the CLI tool. 23 | 24 | ## Getting Started 25 | 26 | Check the [Sample App](https://github.com/osadavc/astro-auth-example) to see how easy it is to get started with Astro Auth or read the [Get Started Guide](getting-started) to learn more about Astro Auth. 27 | 28 | ## Credits 29 | 30 | Special thanks to [Next Auth](https://next-auth.js.org/). Astro Auth is highly inspired by Next Auth. 31 | -------------------------------------------------------------------------------- /docs/src/pages/getting-started/typescript-support.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Using Typescript With Astro Auth 3 | description: Using Typescript With Astro Auth 4 | layout: ../../layouts/MainLayout.astro 5 | --- 6 | 7 | ## TypeScript 8 | 9 | Astro Auth have TypeScript support builtin. If you are an advance user you can import the specific type declarations for the packages that you are using from the `@astro-auth/core` package. 10 | 11 | > Module augmentation is still in development and will come out quickly. 12 | 13 | ## Contribution 14 | 15 | Contributions in any way are welcome especially in Typescript support. 16 | -------------------------------------------------------------------------------- /docs/src/pages/getting-started/using-astro-auth.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Using Astro Auth 3 | description: Using Astro Auth To Build A Login With Google 4 | layout: ../../layouts/MainLayout.astro 5 | --- 6 | 7 | ## Setup Astro Auth 8 | 9 | Read the [Getting Started](/getting-started) section to know how to setup Astro Auth. 10 | 11 | ## Building A Google Login With Astro 12 | 13 | ### 1. Setup 14 | 15 | First will install some more libraries from Astro Auth toolkit to make our lives a little bit easier. Use one of those commands to install the libraries. 16 | 17 | ```bash 18 | npm install @astro-auth/core @astro-auth/client @astro-auth/providers @astro-auth/ui 19 | ``` 20 | 21 | ```bash 22 | yarn add @astro-auth/core @astro-auth/client @astro-auth/providers @astro-auth/ui 23 | ``` 24 | 25 | The client package will make it a lot easier to communicate with the authentication backend. The providers package will provide us with many different **pre configured** OAuth providers. The UI package will provide us with the UI elements that we can use to build our login page. (Please note that the UI package is not mandatory and you can use it or not based on your requirements.) 26 | 27 | At this point you should have all the packages install and ready go. 28 | 29 | ### 2. Edit Astro Auth Config 30 | 31 | Go to your `src/pages/api/auth/[...astroauth].js` file and update the code as follows 32 | 33 | ```js 34 | import AstroAuth from "@astro-auth/core"; 35 | 36 | //Import the providers that you need to use 37 | import { GoogleProvider } from "@astro-auth/providers"; 38 | 39 | export const all = AstroAuth({ 40 | authProviders: [ 41 | // Configure the providers that you need to use 42 | GoogleProvider({ 43 | // You will need to add your client ID and client secret to a .env file 44 | clientId: import.meta.env.GOOGLE_CLIENT_ID, 45 | clientSecret: import.meta.env.GOOGLE_CLIENT_SECRET, 46 | }), 47 | ], 48 | hooks: {}, 49 | }); 50 | ``` 51 | 52 | > Make sure you have added `GOOGLE_CLIENT_ID` and `GOOGLE_CLIENT_SECRET` to your `.env` file. 53 | 54 | > When generating your Google credentials, make sure to set the redirect URL as `YOUR_DOMAIN/api/auth/oauth/google`. So if you're using local host, put the redirect URL as `http://localhost:3000/api/auth/oauth/google`. You can read more about redirect URLs in the providers section 55 | 56 | ### 3. The UI 57 | 58 | For the UI, you can use the Astro themed UI elements that we have provided.(Please note that UI components are currently in beta and it is only working with react but you can definitely use the authentication with other frameworks as well. **However you will need a UI library in order to use the authentication**) 59 | 60 | To use the UI package you need to have React configured for your Astro app. If you don't know how to configure React, please refer to the [Official Guide](https://docs.astro.build/en/core-concepts/framework-components/). 61 | 62 | The create a new component with any name that you like, in this case I am gonna name it as `Login.jsx`. Here are the contents of that file 63 | 64 | ```jsx 65 | import { GoogleButton } from "@astro-auth/ui"; 66 | import { signIn } from "@astro-auth/client"; 67 | 68 | const Login = () => { 69 | return ( 70 |
71 | // If you are using the UI Library this is enough 72 | 73 | // If you are not using the UI Library, you will need to add the following 74 | code 75 | 85 |
86 | ); 87 | }; 88 | 89 | export default Login; 90 | ``` 91 | 92 | Here, you can see we are importing `GoogleButton` from `@astro-auth/ui` and also we are using the `signIn` function from `@astro-auth/client` to sign in with the provider. But you won't need both of them. The signIn call is built in to the GoogleButton component. You will only need to use the `signIn` function if you **don't want to use the UI library**. 93 | 94 | We are gonna pass [this URL](https://youtu.be/dQw4w9WgXcQ) as the callback URL so once user is properly authenticated, Astro Auth will redirect the user to this URL. 95 | 96 | ### 4. Using The Components And Environment Variable Recap 97 | 98 | Make sure to import the Login component in your `.astro` page in-order to display that. Let's recap the requirements for the app before testing it. 99 | 100 | 1. You **MUST** have a `.env` file with the following keys: 101 | 102 | ``` 103 | ASTROAUTH_URL= 104 | ASTROAUTH_SECRET= 105 | 106 | GOOGLE_CLIENT_ID= 107 | GOOGLE_CLIENT_SECRET= 108 | ``` 109 | 110 | 2. Make sure your client id and client secret are correct. You can get them from google cloud console. 111 | 112 | ### 5. Testing 113 | 114 | Run `npm run dev` or `yarn dev` in your terminal and open `localhost:3000` in your browser. You should see a beautiful Astro like Login With Google button. Just click that and go through the process. If you did everything correct, you should be rick rolled at this point. 115 | 116 | > At this point, you should be able to use any login provider to successfully **authenticate** a user in your app. You will learn more about how to **authorize** the logged in users in the next section. 117 | -------------------------------------------------------------------------------- /docs/src/pages/hooks/account.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Account Hook 3 | description: Learn About Account Hook 4 | layout: ../../layouts/MainLayout.astro 5 | --- 6 | 7 | When user initially logs in to the application, Astro Auth will call the `jwt` hook passing everything that it receives from the provider, that is just for the single time that user logs in. 8 | 9 | `account` hook will be called passing the contents in the initially signed JWT token every time that `useUser` client hook is called in the client side. 10 | 11 | > Learn more about `useUser` client hook in the client section 12 | 13 | ## Instructions 14 | 15 | Look at the example below. 16 | 17 | ```js 18 | import AstroAuth from "@astro-auth/core"; 19 | import { GoogleProvider } from "@astro-auth/providers"; 20 | 21 | export const all = AstroAuth({ 22 | authProviders: [ 23 | GoogleProvider({ 24 | clientId: import.meta.env.GOOGLE_CLIENT_ID, 25 | clientSecret: import.meta.env.GOOGLE_CLIENT_SECRET, 26 | }), 27 | ], 28 | hooks: { 29 | jwt: async (user) => { 30 | return { 31 | accessToken: user.accessToken, 32 | user: { 33 | ...user.user, 34 | originalUser: user.user.originalUser, 35 | }, 36 | }; 37 | }, 38 | account: async (user) => { 39 | return { 40 | ...user, 41 | isGood: Math.random() >= 0.5, 42 | }; 43 | }, 44 | }, 45 | }); 46 | ``` 47 | 48 | What this essentially does is add a new property to the user object called `isGood` and set it to a random boolean value every time that the user is accessed from the client side. 49 | -------------------------------------------------------------------------------- /docs/src/pages/hooks/jwt.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: JWT Hook 3 | description: Learn About JWT Hook 4 | layout: ../../layouts/MainLayout.astro 5 | --- 6 | 7 | ## Basics 8 | 9 | Once you logged in with any provider, a brand new JWT token will be generated. This token will be stored in cookies as a HTTP only cookie. This hook will allow you to alter the contents in the token. 10 | 11 | The default JWT token will include the access token from the OAuth provider and very basic information of the user such as user ID, name, email and image. Many OAuth providers will include much more information in the token. By default Astro Auth strip out those data. But by using jwt hook, you can complete alter the contents of the token. 12 | 13 | ## Instruction 14 | 15 | Look at the example below. 16 | 17 | ```js 18 | import AstroAuth from "@astro-auth/core"; 19 | import { GoogleProvider } from "@astro-auth/providers"; 20 | 21 | export const all = AstroAuth({ 22 | authProviders: [ 23 | GoogleProvider({ 24 | clientId: import.meta.env.GOOGLE_CLIENT_ID, 25 | clientSecret: import.meta.env.GOOGLE_CLIENT_SECRET, 26 | }), 27 | ], 28 | hooks: { 29 | // jwt hook has a argument with all the user info 30 | jwt: async (user) => { 31 | return { 32 | accessToken: user.accessToken, 33 | user: { 34 | ...user.user, 35 | originalUser: user.user.originalUser, 36 | }, 37 | }; 38 | }, 39 | }, 40 | }); 41 | ``` 42 | 43 | > You might need to check the provider with `user.provider` and response according to the provider that user is requesting if you have providers that return different data like `CredentialsProvider` or `MetamaskProvider` 44 | 45 | As you can see, you will get the `user` as a argument. You can alter the contents of the token by returning a new object. THe passed user object will look something like this 46 | 47 | ```js 48 | { 49 | accessToken: "", 50 | user: { 51 | id: "", 52 | name: "", 53 | email: "", 54 | image: "", 55 | originalUser: { 56 | id: "", 57 | name: "", 58 | email: "", 59 | image: "", 60 | ... 61 | }, 62 | }, 63 | } 64 | ``` 65 | 66 | You get the very basic info about the user in the user sub object and you will also have a parameter called originalUser which contains the untouched user object from the OAuth provider. 67 | 68 | Using this way, you can customize the contents of the token in any way that you need. 69 | 70 | ### Customize Default Token 71 | 72 | If you want to customize the token by using the default token as a template, you can copy the default token's generating function and modify it according to your choice. Default token generating function is given below. 73 | 74 | ```js 75 | jwt: async (user) => { 76 | return { 77 | accessToken: user.accessToken, 78 | user: { 79 | ...user.user, 80 | originalUser: undefined, 81 | }, 82 | }; 83 | }; 84 | ``` 85 | -------------------------------------------------------------------------------- /docs/src/pages/hooks/redirectError.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Redirect Error Hook 3 | description: Learn About Redirect Error Hook 4 | layout: ../../layouts/MainLayout.astro 5 | --- 6 | 7 | Redirect Error hook is called with the error when any internal error is occurred in the server side. The user will be redirected to the return value of this hook. 8 | 9 | Look at the example below. 10 | 11 | ```js 12 | import AstroAuth from "@astro-auth/core"; 13 | import { GoogleProvider } from "@astro-auth/providers"; 14 | 15 | export const all = AstroAuth({ 16 | authProviders: [ 17 | GoogleProvider({ 18 | clientId: import.meta.env.GOOGLE_CLIENT_ID, 19 | clientSecret: import.meta.env.GOOGLE_CLIENT_SECRET, 20 | }), 21 | ], 22 | hooks: { 23 | redirectError: async (error) => { 24 | console.log(error.message); 25 | return "/"; 26 | }, 27 | }, 28 | }); 29 | ``` 30 | 31 | Here, what is essentially happening is that whenever an error is thrown in the server side, the error message will be logged to the console and the user will be redirected to the `/` route with the error as a query parameter. 32 | -------------------------------------------------------------------------------- /docs/src/pages/hooks/sign-in.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Sign In Hook 3 | description: Learn About Sign In Hook 4 | layout: ../../layouts/MainLayout.astro 5 | --- 6 | 7 | ## Basics 8 | 9 | Once you login with any provider this is the first place that Astro Auth library will look in to. You can decide whether a user should be able to login or not with this hook. 10 | 11 | ## Instructions 12 | 13 | Look at the example below. 14 | 15 | ```js 16 | import AstroAuth from "@astro-auth/core"; 17 | import { GoogleProvider } from "@astro-auth/providers"; 18 | 19 | export const all = AstroAuth({ 20 | authProviders: [ 21 | GoogleProvider({ 22 | clientId: import.meta.env.GOOGLE_CLIENT_ID, 23 | clientSecret: import.meta.env.GOOGLE_CLIENT_SECRET, 24 | }), 25 | ], 26 | hooks: { 27 | // signIn hook has a argument with all the user info 28 | signIn: async (user) => { 29 | return true; 30 | }, 31 | }, 32 | }); 33 | ``` 34 | 35 | As you can see, you will get the `user` as a argument. You can decide whether that particular user should be able to login or not by returning a boolean or string. 36 | 37 | There are few data types that you can return from this hook. 38 | 39 | 1. Truthy value - This will allow the user to login. 40 | 2. Falsy value - This will prevent the user from logging in. 41 | 3. String - This will **NOT** allow user to login. Once user tries to login, he will be redirected to the string that is returned here. 42 | 43 | > As of now once you logged in with the `CredentialProvider`, this hook will **NOT** will be called. but this might change in the near future. 44 | -------------------------------------------------------------------------------- /docs/src/pages/index.astro: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /docs/src/pages/providers/credentials.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Credentials Provider 3 | description: Learn How To Use Credentials Provider 4 | layout: ../../layouts/MainLayout.astro 5 | --- 6 | 7 | ## Instructions 8 | 9 | Optionally, you can use `CredentialsProvider` to authenticate with your custom backend server. In your `signIn` function, you can pass the `provider` parameter as `credentials` and pass the data required to authenticate in the backend server to the `login` object in the `signIn` function. 10 | 11 | ```ts 12 | import AstroAuth from "@astro-auth/core"; 13 | // Import the Credential provider(s) 14 | import { CredentialProvider } from "@astro-auth/providers"; 15 | 16 | export const all = AstroAuth({ 17 | authProviders: [ 18 | CredentialProvider({ 19 | // Here, we are simply checking if the email matches and allow the user to login 20 | authorize: async (properties) => { 21 | if (properties.email == "osadavidath@gmail.com") { 22 | return properties; 23 | } 24 | 25 | return null; 26 | }, 27 | }), 28 | ], 29 | }); 30 | ``` 31 | 32 | Configure the authorize function to handle the authentication process. (the parameter that is passed in will contain all the data you passed to the login function) The function should return the user properties if the user is authenticated or `null` if the user is not authenticated. 33 | 34 | Call the function from the frontend as follows, 35 | 36 | ```ts 37 | import { signIn } from "@astro-auth/client"; 38 | 39 | const signInWithEmailAndPassword = () => { 40 | signIn({ 41 | provider: "credential", 42 | login: { 43 | email: "PASS_FROM_INPUT", 44 | password: "PASS_FROM_INPUT", 45 | }, 46 | }); 47 | }; 48 | ``` 49 | -------------------------------------------------------------------------------- /docs/src/pages/providers/metamask.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Metamask Provider 3 | description: Learn How To Use Metamask Provider 4 | layout: ../../layouts/MainLayout.astro 5 | --- 6 | 7 | ## Instructions 8 | 9 | Optionally, you can use `Metamask Provider` to do a web3 login. This will sign a message with your private key and validate when a user make a sign in request. In your `signIn` function, you can pass the `provider` parameter as `metamask`. 10 | 11 | ```ts 12 | import AstroAuth from "@astro-auth/core"; 13 | // Import the Credential provider(s) 14 | import { MetamaskProvider } from "@astro-auth/providers"; 15 | 16 | export const all = AstroAuth({ 17 | authProviders: [ 18 | Metamask({ 19 | // Here, we are specifying what properties should be in the JWT. In this case, I am gonna pass them all. 20 | authorize: async (properties) => properties, 21 | // This is the message that will be signed and validated in the backend. 22 | signMessage: "Hello From Astro Auth", 23 | }), 24 | ], 25 | }); 26 | ``` 27 | 28 | Configure the authorize function to handle the authentication process. (the parameter that is passed in will contain all the address and signed string) The function should return the user properties if the user is authenticated or `null` if the user should not be authenticated. 29 | 30 | Call the function from the frontend as follows, 31 | 32 | ```ts 33 | import { signIn } from "@astro-auth/client"; 34 | 35 | const signInWithEmailAndPassword = () => { 36 | signIn({ 37 | provider: "metamask", 38 | }); 39 | }; 40 | ``` 41 | 42 | > If user have not installed metamask,the `signIn` function will return an error. 43 | -------------------------------------------------------------------------------- /docs/src/pages/providers/oauth.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: OAuth Providers 3 | description: Learn How To Use OAuth Providers 4 | layout: ../../layouts/MainLayout.astro 5 | --- 6 | 7 | ## Instructions 8 | 9 | OAuth providers will probably be the most common reason for developers to use Astro Auth. Astro Auth supports PKCE security mechanism as well. 10 | 11 | ## Authorized redirect URIs 12 | 13 | The "Authorized redirect URIs" must be configured on your OAuth provider's console. 14 | 15 | - For production: https://{YOUR_DOMAIN}/api/auth/oauth/{PROVIDER} 16 | - For development: http://localhost:3000/api/auth/oauth/{PROVIDER} 17 | 18 | ## OAuth Providers 19 | 20 | Inside the `@astro-auth/provider` package, you will find 8 OAuth providers including their own UI components in the `@astro-auth/ui` package. Here is the list of OAuth providers: 21 | 22 | 1. GoogleProvider 23 | 2. DiscordProvider 24 | 3. TwitterProvider 25 | 4. FacebookProvider 26 | 5. GithubProvider 27 | 6. InstagramProvider 28 | 7. SpotifyProvider 29 | 8. ZoomProvider 30 | 31 | ## Example For Using Github OAuth 32 | 33 | src/pages/api/auth/[...astroauth].ts 34 | 35 | ```ts 36 | import AstroAuth from "@astro-auth/core"; 37 | // Import the OAuth provider(s) 38 | import { GithubProvider } from "@astro-auth/providers"; 39 | 40 | export const all = AstroAuth({ 41 | authProviders: [ 42 | // Configure Your OAuth Providers With Proper Client ID And Secret and optionally the scopes you want to access 43 | GithubProvider({ 44 | clientId: "YOUR_BEAUTIFUL_CLIENT_ID_HERE", 45 | clientSecret: "YOUR_SUPER_SECRET_CLIENT_SECRET_HERE", 46 | scope: ["identify", "email"], 47 | }), 48 | ], 49 | }); 50 | ``` 51 | 52 | and in the front end portion, you can use `signIn` function from `@astro-auth/client` any specific UI component like `GithubButton` from `@astro-auth/ui`. 53 | 54 | To use the `signIn` function, you need to pass the `provider` parameter. 55 | 56 | ```ts 57 | import { signIn } from "@astro-auth/client"; 58 | 59 | const signInWithGithub = () => { 60 | signIn({ 61 | provider: "github", 62 | }); 63 | }; 64 | ``` 65 | 66 | or there is a corresponding UI button for every single OAuth Provider. 67 | 68 | ```tsx 69 | import { GithubButton } from "@astro-auth/ui"; 70 | 71 | const Login = () => { 72 | return ( 73 |
74 | 75 |
76 | ); 77 | }; 78 | 79 | export default Login; 80 | ``` 81 | -------------------------------------------------------------------------------- /docs/src/pages/state-store/other-frameworks.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Using User State Inside Other Frameworks 3 | description: Learn How To Consume User State Inside Of Frameworks Other Than React 4 | layout: ../../layouts/MainLayout.astro 5 | --- 6 | 7 | ## Instructions 8 | 9 | At the moment, in frameworks other than react, there is no way to preserve the state inside of the application, so you have to fetch the user from the server every time. Here is how you do it in vue. 10 | 11 | ```html 12 | 26 | 27 | 30 | ``` 31 | 32 | You can use the same method to do the same thing in other frameworks. 33 | 34 | > **DO NOT USE THIS METHOD IN REACT.** 35 | -------------------------------------------------------------------------------- /docs/src/pages/state-store/react.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Using User State Inside Of React Components 3 | description: Learn How To Consume User State Inside React Components 4 | layout: ../../layouts/MainLayout.astro 5 | --- 6 | 7 | ## Instructions 8 | 9 | Normally when you consume logged in user's state in a component, you have to drill props with the user from the server side. But by using what is so called `state store`, you don't have to. (This feature is experimental and works best with react, other frameworks are supported too.) 10 | 11 | First, run this command in the terminal to generate a state store: 12 | 13 | ```bash 14 | npx @astro-auth/cli -state 15 | ``` 16 | 17 | Then you have to use that component in each and every page you want to consume the user state. 18 | 19 | ```js 20 | 21 | --- 22 | import { getUser } from "@astro-auth/core"; 23 | import UserStore from "../components/UserStore"; 24 | 25 | const user = getUser(); 26 | --- 27 | 28 | 29 | 30 | 31 | 32 | Astro 33 | 34 | 35 | 36 | 37 | 38 | 39 | ``` 40 | 41 | What this will ultimately do is, it'll store the user in a state store, and then you can use the state in a React Component like that. 42 | 43 | ```jsx 44 | import { ReactStateStore } from "@astro-auth/client"; 45 | 46 | const ReactComponent = () => { 47 | const user = ReactStateStore.useUser({ update: true }); 48 | 49 | return
; 50 | }; 51 | 52 | export default ReactComponent; 53 | ``` 54 | 55 | You can call the `useUser` hook with `update: true` to fetch a fresh user by passing the old user through the `account` hook. If you don't want to update the user, you can pass `update: false` or not pass anything at all and the user in the state store will be returned. 56 | 57 | > Check [other](/state-store/other-frameworks) frameworks section to know how to access user state from other frameworks. 58 | -------------------------------------------------------------------------------- /docs/src/pages/ui-library/button.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Button Component 3 | description: Learn About Button Component 4 | layout: ../../layouts/MainLayout.astro 5 | --- 6 | 7 | Button is a component that is included in the `@astro-auth/ui` library and it looks exactly similar to the buttons in the [astro.build](https://astro.build) official website. 8 | 9 | You can use the button like this, 10 | 11 | ```jsx 12 | import { Button } from "@astro-auth/ui"; 13 | 14 | const ButtonSection = () => { 15 | return ; 16 | }; 17 | ``` 18 | -------------------------------------------------------------------------------- /docs/src/pages/ui-library/instructions.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Instruction 3 | description: Instruction to use the UI library 4 | layout: ../../layouts/MainLayout.astro 5 | --- 6 | 7 | ### Introduction 8 | 9 | Install `@astro-auth/ui` package to use the UI library. 10 | 11 | > Before proceeding, please note that the UI library is still in beta and only works with react. 12 | 13 | To load the the stylesheets and font properly, you need to modify your `astro.config.mjs` file like this, 14 | 15 | ```js 16 | import { defineConfig } from "astro/config"; 17 | import vercel from "@astrojs/vercel/serverless"; 18 | import { astroAuthComponents } from "@astro-auth/ui"; 19 | 20 | export default defineConfig({ 21 | integrations: [react(), astroAuthComponents()], 22 | adapter: vercel(), 23 | experimental: { 24 | integrations: true, 25 | }, 26 | }); 27 | ``` 28 | 29 | You will need react and astroAuthComponents as integrations and experimental.integrations to be enabled. 30 | -------------------------------------------------------------------------------- /docs/src/pages/ui-library/provider-buttons.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Provider Button Components 3 | description: Learn About Provider Button Components 4 | layout: ../../layouts/MainLayout.astro 5 | --- 6 | 7 | Provider Buttons are components that are included in the `@astro-auth/ui` library and the button looks exactly similar to the Buttons in the [astro.build](https://astro.build) official website. You have each Provider Button for each provider that is included in the `@astro-auth/providers` library. 8 | 9 | You can use the Google button like this, 10 | 11 | ```jsx 12 | import { GoogleButton } from "@astro-auth/ui"; 13 | 14 | const ProviderButtonSection = () => { 15 | return ; 16 | }; 17 | ``` 18 | 19 | Provider Buttons takes 3 optional props 20 | 21 | 1. `onClick`: Function that is called when the button is clicked. Defaults to the login function. 22 | 2. `callback`: The route that user will be redirected in a successful login attempt. Defaults to `/`. 23 | 3. `children`: The text that is displayed on the button. Defaults to the provider name. 24 | 25 | > Read more about provider buttons in the each provider page of the documentation. 26 | -------------------------------------------------------------------------------- /docs/src/pages/ui-library/title.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Title Component 3 | description: Learn About Title Component 4 | layout: ../../layouts/MainLayout.astro 5 | --- 6 | 7 | Title is a component that is included in the `@astro-auth/ui` library and it looks exactly similar to the title text in the [astro.build](https://astro.build) official website. 8 | 9 | You can use the title like this, 10 | 11 | ```jsx 12 | import { Title } from "@astro-auth/ui"; 13 | 14 | const TitleSection = () => { 15 | return Click Me; 16 | }; 17 | ``` 18 | -------------------------------------------------------------------------------- /docs/src/styles/index.css: -------------------------------------------------------------------------------- 1 | * { 2 | box-sizing: border-box; 3 | margin: 0; 4 | } 5 | 6 | /* Global focus outline reset */ 7 | *:focus:not(:focus-visible) { 8 | outline: none; 9 | } 10 | 11 | :root { 12 | --user-font-scale: 1rem - 16px; 13 | --max-width: calc(100% - 1rem); 14 | } 15 | 16 | @media (min-width: 50em) { 17 | :root { 18 | --max-width: 46em; 19 | } 20 | } 21 | 22 | body { 23 | display: flex; 24 | flex-direction: column; 25 | min-height: 100vh; 26 | font-family: var(--font-body); 27 | font-size: 1rem; 28 | font-size: clamp(0.9rem, 0.75rem + 0.375vw + var(--user-font-scale), 1rem); 29 | line-height: 1.5; 30 | max-width: 100vw; 31 | overflow-x: hidden; 32 | overflow-y: overlay; 33 | } 34 | 35 | ::-webkit-scrollbar { 36 | width: 16px; 37 | background-color: transparent; 38 | } 39 | 40 | ::-webkit-scrollbar-thumb { 41 | border: 5px solid rgba(0, 0, 0, 0); 42 | background-clip: padding-box; 43 | border-radius: 9999px; 44 | background-color: #555; 45 | } 46 | ::-webkit-scrollbar-thumb:hover { 47 | border: 5px solid rgba(0, 0, 0, 0); 48 | background-clip: padding-box; 49 | border-radius: 9999px; 50 | background-color: #aaaaaa; 51 | } 52 | 53 | nav ul { 54 | list-style: none; 55 | padding: 0; 56 | } 57 | 58 | .content > section > * + * { 59 | margin-top: 1.25rem; 60 | } 61 | 62 | .content > section > :first-child { 63 | margin-top: 0; 64 | } 65 | 66 | /* Typography */ 67 | h1, 68 | h2, 69 | h3, 70 | h4, 71 | h5, 72 | h6 { 73 | margin-bottom: 1rem; 74 | font-weight: bold; 75 | line-height: 1; 76 | } 77 | 78 | h1, 79 | h2 { 80 | max-width: 40ch; 81 | } 82 | 83 | :is(h2, h3):not(:first-child) { 84 | margin-top: 3rem; 85 | } 86 | 87 | :is(h4, h5, h6):not(:first-child) { 88 | margin-top: 2rem; 89 | } 90 | 91 | h1 { 92 | font-size: 3.25rem; 93 | font-weight: 800; 94 | } 95 | 96 | h2 { 97 | font-size: 2.5rem; 98 | } 99 | 100 | h3 { 101 | font-size: 1.75rem; 102 | } 103 | 104 | h4 { 105 | font-size: 1.3rem; 106 | } 107 | 108 | h5 { 109 | font-size: 1rem; 110 | } 111 | 112 | p { 113 | line-height: 1.65em; 114 | } 115 | 116 | .content ul { 117 | line-height: 1.1em; 118 | } 119 | 120 | p, 121 | .content ul { 122 | color: var(--theme-text-light); 123 | } 124 | 125 | small, 126 | .text_small { 127 | font-size: 0.833rem; 128 | } 129 | 130 | a { 131 | color: var(--theme-text-accent); 132 | font-weight: 400; 133 | text-underline-offset: 0.08em; 134 | align-items: center; 135 | gap: 0.5rem; 136 | } 137 | 138 | article > section :is(ul, ol) > * + * { 139 | margin-top: 0.75rem; 140 | } 141 | 142 | article > section nav :is(ul, ol) > * + * { 143 | margin-top: inherit; 144 | } 145 | 146 | article > section li > :is(p, pre, blockquote):not(:first-child) { 147 | margin-top: 1rem; 148 | } 149 | 150 | article > section :is(ul, ol) { 151 | padding-left: 1em; 152 | } 153 | 154 | article > section nav :is(ul, ol) { 155 | padding-left: inherit; 156 | } 157 | 158 | article > section nav { 159 | margin-top: 1rem; 160 | margin-bottom: 2rem; 161 | } 162 | 163 | article > section ::marker { 164 | font-weight: bold; 165 | color: var(--theme-text-light); 166 | } 167 | 168 | article > section iframe { 169 | width: 100%; 170 | height: auto; 171 | aspect-ratio: 16 / 9; 172 | } 173 | 174 | a > code { 175 | position: relative; 176 | color: var(--theme-text-accent); 177 | background: transparent; 178 | text-underline-offset: var(--padding-block); 179 | } 180 | 181 | a > code::before { 182 | content: ''; 183 | position: absolute; 184 | top: 0; 185 | right: 0; 186 | bottom: 0; 187 | left: 0; 188 | display: block; 189 | background: var(--theme-accent); 190 | opacity: var(--theme-accent-opacity); 191 | border-radius: var(--border-radius); 192 | } 193 | 194 | a:hover, 195 | a:focus { 196 | text-decoration: underline; 197 | } 198 | 199 | a:focus { 200 | outline: 2px solid currentColor; 201 | outline-offset: 0.25em; 202 | } 203 | 204 | strong { 205 | font-weight: 600; 206 | color: inherit; 207 | } 208 | 209 | /* Supporting Content */ 210 | 211 | code { 212 | --border-radius: 3px; 213 | --padding-block: 0.2rem; 214 | --padding-inline: 0.33rem; 215 | 216 | font-family: var(--font-mono); 217 | font-size: 0.85em; 218 | color: inherit; 219 | background-color: var(--theme-code-inline-bg); 220 | padding: var(--padding-block) var(--padding-inline); 221 | margin: calc(var(--padding-block) * -1) -0.125em; 222 | border-radius: var(--border-radius); 223 | word-break: break-word; 224 | } 225 | 226 | pre.astro-code > code { 227 | all: unset; 228 | } 229 | 230 | pre > code { 231 | font-size: 1em; 232 | } 233 | 234 | table, 235 | pre { 236 | position: relative; 237 | --padding-block: 1rem; 238 | --padding-inline: 2rem; 239 | padding: var(--padding-block) var(--padding-inline); 240 | padding-right: calc(var(--padding-inline) * 2); 241 | margin-left: calc(var(--padding-inline) * -1); 242 | margin-right: calc(var(--padding-inline) * -1); 243 | font-family: var(--font-mono); 244 | 245 | line-height: 1.5; 246 | font-size: 0.85em; 247 | overflow-y: hidden; 248 | overflow-x: auto; 249 | } 250 | 251 | table { 252 | width: 100%; 253 | padding: var(--padding-block) 0; 254 | margin: 0; 255 | border-collapse: collapse; 256 | } 257 | 258 | /* Zebra striping */ 259 | tr:nth-of-type(odd) { 260 | background: var(--theme-bg-hover); 261 | } 262 | th { 263 | background: var(--color-black); 264 | color: var(--theme-color); 265 | font-weight: bold; 266 | } 267 | td, 268 | th { 269 | padding: 6px; 270 | text-align: left; 271 | } 272 | 273 | pre { 274 | background-color: var(--theme-code-bg); 275 | color: var(--theme-code-text); 276 | } 277 | 278 | blockquote code { 279 | background-color: var(--theme-bg); 280 | } 281 | 282 | @media (min-width: 37.75em) { 283 | pre { 284 | --padding-inline: 1.25rem; 285 | border-radius: 8px; 286 | margin-left: 0; 287 | margin-right: 0; 288 | } 289 | } 290 | 291 | blockquote { 292 | margin: 2rem 0; 293 | padding: 1.25em 1.5rem; 294 | border-left: 3px solid var(--theme-text-light); 295 | background-color: var(--theme-bg-offset); 296 | border-radius: 0 0.25rem 0.25rem 0; 297 | line-height: 1.7; 298 | } 299 | 300 | img { 301 | max-width: 100%; 302 | } 303 | 304 | .flex { 305 | display: flex; 306 | align-items: center; 307 | } 308 | 309 | button { 310 | display: flex; 311 | align-items: center; 312 | justify-items: center; 313 | gap: 0.25em; 314 | padding: 0.33em 0.67em; 315 | border: 0; 316 | background: var(--theme-bg); 317 | display: flex; 318 | font-size: 1rem; 319 | align-items: center; 320 | gap: 0.25em; 321 | border-radius: 99em; 322 | color: var(--theme-text); 323 | background-color: var(--theme-bg); 324 | } 325 | 326 | h2.heading { 327 | font-size: 1rem; 328 | font-weight: 700; 329 | padding: 0.1rem 1rem; 330 | text-transform: uppercase; 331 | margin-bottom: 0.5rem; 332 | } 333 | 334 | .header-link { 335 | font-size: 1rem; 336 | padding: 0.1rem 0 0.1rem 1rem; 337 | border-left: 4px solid var(--theme-divider); 338 | } 339 | 340 | .header-link:hover, 341 | .header-link:focus { 342 | border-left-color: var(--theme-accent); 343 | color: var(--theme-accent); 344 | } 345 | .header-link:focus-within { 346 | color: var(--theme-text-light); 347 | border-left-color: hsla(var(--color-gray-40), 1); 348 | } 349 | .header-link svg { 350 | opacity: 0.6; 351 | } 352 | .header-link:hover svg { 353 | opacity: 0.8; 354 | } 355 | .header-link a { 356 | display: inline-flex; 357 | gap: 0.5em; 358 | width: 100%; 359 | padding: 0.15em 0 0.15em 0; 360 | } 361 | 362 | .header-link.depth-3 { 363 | padding-left: 2rem; 364 | } 365 | .header-link.depth-4 { 366 | padding-left: 3rem; 367 | } 368 | 369 | .header-link a { 370 | font: inherit; 371 | color: inherit; 372 | text-decoration: none; 373 | } 374 | 375 | /* Screenreader Only Text */ 376 | .sr-only { 377 | position: absolute; 378 | width: 1px; 379 | height: 1px; 380 | padding: 0; 381 | margin: -1px; 382 | overflow: hidden; 383 | clip: rect(0, 0, 0, 0); 384 | white-space: nowrap; 385 | border-width: 0; 386 | } 387 | 388 | .focus\:not-sr-only:focus, 389 | .focus\:not-sr-only:focus-visible { 390 | position: static; 391 | width: auto; 392 | height: auto; 393 | padding: 0; 394 | margin: 0; 395 | overflow: visible; 396 | clip: auto; 397 | white-space: normal; 398 | } 399 | 400 | :target { 401 | scroll-margin: calc(var(--theme-sidebar-offset, 5rem) + 2rem) 0 2rem; 402 | } 403 | -------------------------------------------------------------------------------- /docs/src/styles/theme.css: -------------------------------------------------------------------------------- 1 | @import url("https://fonts.googleapis.com/css2?family=Inter:wght@400;500;700;900&display=swap"); 2 | 3 | :root { 4 | --font-fallback: -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial, 5 | sans-serif, Apple Color Emoji, Segoe UI Emoji; 6 | --font-body: "Inter", var(--font-fallback); 7 | --font-mono: "IBM Plex Mono", Consolas, "Andale Mono WT", "Andale Mono", 8 | "Lucida Console", "Lucida Sans Typewriter", "DejaVu Sans Mono", 9 | "Bitstream Vera Sans Mono", "Liberation Mono", "Nimbus Mono L", Monaco, 10 | "Courier New", Courier, monospace; 11 | 12 | /* 13 | * Variables with --color-base prefix define 14 | * the hue, and saturation values to be used for 15 | * hsla colors. 16 | * 17 | * ex: 18 | * 19 | * --color-base-{color}: {hue}, {saturation}; 20 | * 21 | */ 22 | 23 | --color-base-white: 0, 0%; 24 | --color-base-black: 240, 100%; 25 | --color-base-gray: 215, 14%; 26 | --color-base-blue: 212, 100%; 27 | --color-base-blue-dark: 212, 72%; 28 | --color-base-green: 158, 79%; 29 | --color-base-orange: 22, 100%; 30 | --color-base-purple: 269, 79%; 31 | --color-base-red: 351, 100%; 32 | --color-base-yellow: 41, 100%; 33 | 34 | /* 35 | * Color palettes are made using --color-base 36 | * variables, along with a lightness value to 37 | * define different variants. 38 | * 39 | */ 40 | 41 | --color-gray-5: var(--color-base-gray), 5%; 42 | --color-gray-10: var(--color-base-gray), 10%; 43 | --color-gray-20: var(--color-base-gray), 20%; 44 | --color-gray-30: var(--color-base-gray), 30%; 45 | --color-gray-40: var(--color-base-gray), 40%; 46 | --color-gray-50: var(--color-base-gray), 50%; 47 | --color-gray-60: var(--color-base-gray), 60%; 48 | --color-gray-70: var(--color-base-gray), 70%; 49 | --color-gray-80: var(--color-base-gray), 80%; 50 | --color-gray-90: var(--color-base-gray), 90%; 51 | --color-gray-95: var(--color-base-gray), 95%; 52 | 53 | --color-blue: var(--color-base-blue), 61%; 54 | --color-blue-dark: var(--color-base-blue-dark), 39%; 55 | --color-green: var(--color-base-green), 42%; 56 | --color-orange: var(--color-base-orange), 50%; 57 | --color-purple: var(--color-base-purple), 54%; 58 | --color-red: var(--color-base-red), 54%; 59 | --color-yellow: var(--color-base-yellow), 59%; 60 | } 61 | 62 | :root { 63 | color-scheme: light; 64 | --theme-accent: hsla(var(--color-blue), 1); 65 | --theme-text-accent: hsla(var(--color-blue), 1); 66 | --theme-accent-opacity: 0.15; 67 | --theme-divider: hsla(var(--color-gray-95), 1); 68 | --theme-text: hsla(var(--color-gray-10), 1); 69 | --theme-text-light: hsla(var(--color-gray-40), 1); 70 | /* @@@: not used anywhere */ 71 | --theme-text-lighter: hsla(var(--color-gray-80), 1); 72 | --theme-bg: hsla(var(--color-base-white), 100%, 1); 73 | --theme-bg-hover: hsla(var(--color-gray-95), 1); 74 | --theme-bg-offset: hsla(var(--color-gray-90), 1); 75 | --theme-bg-accent: hsla(var(--color-blue), var(--theme-accent-opacity)); 76 | --theme-code-inline-bg: hsla(var(--color-gray-95), 1); 77 | --theme-code-inline-text: var(--theme-text); 78 | --theme-code-bg: hsla(217, 19%, 27%, 1); 79 | --theme-code-text: hsla(var(--color-gray-95), 1); 80 | --theme-navbar-bg: hsla(var(--color-base-white), 100%, 1); 81 | --theme-navbar-height: 6rem; 82 | --theme-selection-color: hsla(var(--color-blue), 1); 83 | --theme-selection-bg: hsla(var(--color-blue), var(--theme-accent-opacity)); 84 | } 85 | 86 | body { 87 | background: var(--theme-bg); 88 | color: var(--theme-text); 89 | } 90 | 91 | :root.theme-dark { 92 | color-scheme: dark; 93 | --theme-accent-opacity: 0.15; 94 | --theme-accent: hsla(var(--color-blue), 1); 95 | --theme-text-accent: hsla(var(--color-blue), 1); 96 | --theme-divider: hsla(var(--color-gray-10), 1); 97 | --theme-text: hsla(var(--color-gray-90), 1); 98 | --theme-text-light: hsla(var(--color-gray-80), 1); 99 | 100 | /* @@@: not used anywhere */ 101 | --theme-text-lighter: hsla(var(--color-gray-40), 1); 102 | --theme-bg: rgb(0, 0, 0); 103 | --theme-bg-hover: hsla(var(--color-gray-40), 1); 104 | --theme-bg-offset: hsla(var(--color-gray-5), 1); 105 | --theme-code-inline-bg: hsla(var(--color-gray-10), 1); 106 | --theme-code-inline-text: hsla(var(--color-base-white), 100%, 1); 107 | --theme-code-bg: hsla(var(--color-gray-5), 1); 108 | --theme-code-text: hsla(var(--color-base-white), 100%, 1); 109 | --theme-navbar-bg: rgb(14, 14, 14) 110 | --theme-selection-color: hsla(var(--color-base-white), 100%, 1); 111 | --theme-selection-bg: hsla(var(--color-purple), var(--theme-accent-opacity)); 112 | 113 | /* DocSearch [Algolia] */ 114 | --docsearch-modal-background: var(--theme-bg); 115 | --docsearch-searchbox-focus-background: var(--theme-divider); 116 | --docsearch-footer-background: var(--theme-divider); 117 | --docsearch-text-color: var(--theme-text); 118 | --docsearch-hit-background: var(--theme-divider); 119 | --docsearch-hit-shadow: none; 120 | --docsearch-hit-color: var(--theme-text); 121 | --docsearch-footer-shadow: inset 0 2px 10px #000; 122 | --docsearch-modal-shadow: inset 0 0 8px #000; 123 | } 124 | 125 | ::selection { 126 | color: var(--theme-selection-color); 127 | background-color: var(--theme-selection-bg); 128 | } 129 | -------------------------------------------------------------------------------- /docs/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2020", 4 | "module": "esnext", 5 | "jsx": "preserve", 6 | "moduleResolution": "node" 7 | }, 8 | "moduleResolution": "node" 9 | } 10 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "workspaces": [ 4 | "packages/*", 5 | "apps/dev", 6 | "docs" 7 | ], 8 | "scripts": { 9 | "dev:packages": "concurrently \"cd packages/astro-auth-client && yarn dev\" \"cd packages/astro-auth-core && yarn dev\" \"cd packages/astro-auth-providers && yarn dev\" \"cd packages/astro-auth-ui && yarn dev\" ", 10 | "build:packages": "concurrently \"cd packages/astro-auth-client && yarn build\" \"cd packages/astro-auth-core && yarn build\" \"cd packages/astro-auth-providers && yarn build\" \"cd packages/astro-auth-ui && yarn build\" ", 11 | "publish:packages": "concurrently \"cd packages/astro-auth-client && npm publish\" \"cd packages/astro-auth-core && npm publish\" \"cd packages/astro-auth-providers && npm publish\" \"cd packages/astro-auth-ui && npm publish\" " 12 | }, 13 | "dependencies": { 14 | "concurrently": "^7.1.0" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /packages/astro-auth-cli/README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 |

Astro Auth CLI

4 | 5 |

6 | 7 |

8 | 9 | ## Getting Started 10 | 11 | Run the following command inside your project directory: 12 | 13 | ``` 14 | npx @astro-auth/cli 15 | ``` 16 | 17 | ## Documentation 18 | 19 | Read the documentation at 20 | 21 | ## Contributing 22 | 23 | We're open to all community contributions, just make a pull request! 24 | -------------------------------------------------------------------------------- /packages/astro-auth-cli/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@astro-auth/cli", 3 | "version": "1.2.5", 4 | "description": "The CLI tool of the Astro Auth library", 5 | "main": "dist/index.js", 6 | "bin": "dist/index.js", 7 | "scripts": { 8 | "start": "node dist/index.js", 9 | "dev": "tsc -w", 10 | "build": "tsc" 11 | }, 12 | "keywords": [ 13 | "astro", 14 | "astro-auth", 15 | "astro-auth-cli", 16 | "authentication" 17 | ], 18 | "author": "", 19 | "license": "ISC", 20 | "devDependencies": { 21 | "@types/inquirer": "^8.2.1", 22 | "typescript": "^4.6.3" 23 | }, 24 | "dependencies": { 25 | "inquirer": "^8.2.2" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /packages/astro-auth-cli/src/index.ts: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env node 2 | 3 | import inquirer from "inquirer"; 4 | import path from "path"; 5 | import fs from "fs"; 6 | 7 | const isNPMInitialized = fs.existsSync( 8 | path.join(process.cwd(), "package.json") 9 | ); 10 | 11 | const isProjectTS = fs.existsSync(path.join(process.cwd(), "tsconfig.json")); 12 | 13 | const configPath = isProjectTS 14 | ? path.join(process.cwd(), "src", "pages", "api", "auth", "[...astroauth].ts") 15 | : path.join( 16 | process.cwd(), 17 | "src", 18 | "pages", 19 | "api", 20 | "auth", 21 | "[...astroauth].js" 22 | ); 23 | 24 | const createConfigFileFlow = () => { 25 | inquirer 26 | .prompt([ 27 | { 28 | type: "confirm", 29 | name: "create", 30 | message: "✨ Do you want to create a astroauth config file ?", 31 | default: false, 32 | }, 33 | ]) 34 | .then((answers: { create: boolean }) => { 35 | if (answers.create) { 36 | createConfigFile(); 37 | } else { 38 | console.log("Goodbye! 👋"); 39 | } 40 | }); 41 | }; 42 | 43 | const createConfigFile = () => { 44 | const config = `import AstroAuth from "@astro-auth/core"; 45 | // Import Providers Here 46 | 47 | export const all = AstroAuth({ 48 | authProviders: [ 49 | // Add Your Providers Here 50 | ], 51 | });`; 52 | 53 | fs.mkdirSync(path.join(process.cwd(), "src", "pages", "api", "auth"), { 54 | recursive: true, 55 | }); 56 | 57 | fs.writeFileSync(configPath, config, "utf8"); 58 | console.log("✅ astroauth config file created successfully"); 59 | }; 60 | 61 | const createStateIsland = (defaultConfirmation?: boolean) => { 62 | inquirer 63 | .prompt([ 64 | { 65 | type: "confirm", 66 | name: "state_island", 67 | message: 68 | "✨ Do you want to setup state-island? (Still Only Working With React)", 69 | default: defaultConfirmation || false, 70 | }, 71 | ]) 72 | .then((answers: { state_island: boolean }) => { 73 | const jsStateIsland = `import { ReactStateStore } from "@astro-auth/client"; 74 | 75 | const UserStore = ({ user }) => { 76 | return ; 77 | }; 78 | 79 | export default UserStore; 80 | `; 81 | 82 | const tsStateIsland = `import { ReactStateStore } from "@astro-auth/client"; 83 | 84 | const UserStore = ({ user }: any) => { 85 | return ; 86 | }; 87 | 88 | export default UserStore; 89 | `; 90 | 91 | fs.mkdirSync(path.join(process.cwd(), "src", "components", "UserStore"), { 92 | recursive: true, 93 | }); 94 | 95 | if (answers.state_island) { 96 | if (isProjectTS) { 97 | fs.writeFileSync( 98 | path.join( 99 | process.cwd(), 100 | "src", 101 | "components", 102 | "UserStore", 103 | "index.tsx" 104 | ), 105 | tsStateIsland, 106 | "utf8" 107 | ); 108 | } else { 109 | fs.writeFileSync( 110 | path.join( 111 | process.cwd(), 112 | "src", 113 | "components", 114 | "UserStore", 115 | "index.jsx" 116 | ), 117 | jsStateIsland, 118 | "utf8" 119 | ); 120 | } 121 | 122 | const packageJSON = fs 123 | .readFileSync(path.join(process.cwd(), "package.json")) 124 | .toString(); 125 | 126 | const isAstroClientInstalled = ( 127 | Object.keys(JSON.parse(packageJSON).dependencies) as Array 128 | ).find((item) => item == "@astro-auth/client"); 129 | 130 | if (!isAstroClientInstalled) { 131 | return console.log("🚨 You need to install @astro-auth/client first"); 132 | } 133 | 134 | console.log("✅ State island setup successfully"); 135 | console.log( 136 | "📃 Read the documentation (https://astro-auth.weoffersolution.com/state-store/react) for more info about using the state island" 137 | ); 138 | } else { 139 | console.log("Goodbye! 👋"); 140 | } 141 | }); 142 | }; 143 | 144 | const main = () => { 145 | if (!isNPMInitialized) { 146 | return console.log("🚨 You need to run `npm init` first"); 147 | } 148 | 149 | const isPackageJSON = fs.existsSync(path.join(process.cwd(), "package.json")); 150 | 151 | if (!isPackageJSON) { 152 | return console.log("🚨 You need to run `npm init` first"); 153 | } 154 | 155 | const packageJSON = fs 156 | .readFileSync(path.join(process.cwd(), "package.json")) 157 | .toString(); 158 | 159 | const isDevDeps = JSON.parse(packageJSON).devDependencies; 160 | 161 | if (!isDevDeps) { 162 | return console.log( 163 | "🚨 It doesn't look like a project that supports astroauth" 164 | ); 165 | } 166 | 167 | const isAstroInstalled = ( 168 | Object.keys(JSON.parse(packageJSON)?.devDependencies) as Array 169 | ).find((item) => item == "astro"); 170 | 171 | if (!isAstroInstalled) { 172 | return console.log( 173 | "🚨 It doesn't look like a project that supports astroauth" 174 | ); 175 | } 176 | 177 | const isAstroAuthCoreInstalled = ( 178 | Object.keys(JSON.parse(packageJSON).dependencies) as Array 179 | ).find((item) => item == "@astro-auth/core"); 180 | 181 | if (!isAstroAuthCoreInstalled) { 182 | return console.log("🚨 You need to install @astro-auth/core first"); 183 | } 184 | 185 | const isConfigExists = 186 | fs.existsSync( 187 | path.join( 188 | process.cwd(), 189 | "src", 190 | "pages", 191 | "api", 192 | "auth", 193 | "[...astroauth].ts" 194 | ) 195 | ) || 196 | fs.existsSync( 197 | path.join( 198 | process.cwd(), 199 | "src", 200 | "pages", 201 | "api", 202 | "auth", 203 | "[...astroauth].js" 204 | ) 205 | ); 206 | 207 | if (isConfigExists) { 208 | inquirer 209 | .prompt([ 210 | { 211 | type: "confirm", 212 | name: "overwrite", 213 | message: 214 | "🚨 astroauth config file already exists! Would you like to overwrite it?", 215 | default: false, 216 | }, 217 | ]) 218 | .then((answers: { overwrite: boolean }) => { 219 | if (answers.overwrite) { 220 | createConfigFile(); 221 | } else { 222 | console.log("Goodbye! 👋"); 223 | } 224 | }); 225 | } else { 226 | createConfigFileFlow(); 227 | } 228 | }; 229 | 230 | if (process.argv[2] === "-state") { 231 | createStateIsland(true); 232 | } else { 233 | main(); 234 | } 235 | -------------------------------------------------------------------------------- /packages/astro-auth-cli/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES5", 4 | "module": "CommonJS", 5 | "esModuleInterop": true, 6 | "forceConsistentCasingInFileNames": true, 7 | "strict": true, 8 | "declaration": true, 9 | "skipLibCheck": true, 10 | "outDir": "dist", 11 | "rootDir": "src", 12 | "moduleResolution": "node" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /packages/astro-auth-client/README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 |

Astro Auth Client

4 | 5 |

6 | 7 |

8 | 9 | ## Getting Started 10 | 11 | Run one of the following command inside your project directory to install the package: 12 | 13 | ``` 14 | yarn add @astro-auth/client 15 | 16 | or 17 | 18 | npm i @astro-auth/client 19 | ``` 20 | 21 | ## Documentation 22 | 23 | Read the documentation at 24 | 25 | ## Contributing 26 | 27 | We're open to all community contributions, just make a pull request! 28 | -------------------------------------------------------------------------------- /packages/astro-auth-client/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@astro-auth/client", 3 | "version": "1.0.5", 4 | "main": "dist/index.js", 5 | "type": "module", 6 | "description": "The client package of the Astro Auth library", 7 | "author": "Osada Vidath ", 8 | "license": "MIT", 9 | "files": [ 10 | "dist" 11 | ], 12 | "keywords": [ 13 | "astro-component", 14 | "astro-auth", 15 | "astro-auth-client" 16 | ], 17 | "module": "dist/index.js", 18 | "types": "dist/index.d.ts", 19 | "scripts": { 20 | "build": "rollup -c", 21 | "dev": "rollup -c -w" 22 | }, 23 | "devDependencies": { 24 | "@rollup/plugin-commonjs": "^21.0.3", 25 | "@rollup/plugin-node-resolve": "^13.2.0", 26 | "@rollup/plugin-typescript": "^8.3.2", 27 | "rollup": "^2.70.1", 28 | "rollup-plugin-dts": "^4.2.1", 29 | "rollup-plugin-peer-deps-external": "^2.2.4", 30 | "rollup-plugin-terser": "^7.0.2", 31 | "typescript": "^4.6.3" 32 | }, 33 | "peerDependencies": { 34 | "react": "^18.0.0", 35 | "react-dom": "^18.0.0" 36 | }, 37 | "dependencies": { 38 | "@astro-auth/types": "^1.0.2", 39 | "@metamask/providers": "^8.1.1", 40 | "@nanostores/react": "^0.2.0", 41 | "ethers": "^5.6.6", 42 | "nanostores": "^0.5.12" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /packages/astro-auth-client/rollup.config.js: -------------------------------------------------------------------------------- 1 | import resolve from "@rollup/plugin-node-resolve"; 2 | import typescript from "@rollup/plugin-typescript"; 3 | import commonjs from "@rollup/plugin-commonjs"; 4 | import dts from "rollup-plugin-dts"; 5 | import { terser } from "rollup-plugin-terser"; 6 | import peerDepsExternal from "rollup-plugin-peer-deps-external"; 7 | 8 | const packageJson = require("./package.json"); 9 | 10 | export default [ 11 | { 12 | input: "src/index.ts", 13 | output: [ 14 | { 15 | file: packageJson.module, 16 | format: "esm", 17 | sourcemap: true, 18 | }, 19 | ], 20 | plugins: [ 21 | peerDepsExternal(), 22 | resolve({ 23 | preferBuiltins: true, 24 | }), 25 | commonjs(), 26 | typescript({ tsconfig: "./tsconfig.json" }), 27 | terser(), 28 | ], 29 | }, 30 | { 31 | input: "dist/types/index.d.ts", 32 | output: [{ file: "dist/index.d.ts", format: "esm" }], 33 | plugins: [dts()], 34 | external: [/\.css$/], 35 | }, 36 | ]; 37 | -------------------------------------------------------------------------------- /packages/astro-auth-client/src/env.d.ts: -------------------------------------------------------------------------------- 1 | interface ImportMetaEnv { 2 | readonly ASTROAUTH_URL: string; 3 | readonly ASTROAUTH_SECRET: string; 4 | } 5 | 6 | interface ImportMeta { 7 | readonly env: ImportMetaEnv; 8 | } 9 | -------------------------------------------------------------------------------- /packages/astro-auth-client/src/index.ts: -------------------------------------------------------------------------------- 1 | import signOut from "./signOut"; 2 | import signIn from "./signIn"; 3 | import ReactStateStore from "./stateStores/ReactStateStore"; 4 | import useUser from "./stateStores/store/useUser"; 5 | 6 | export { signOut, signIn, ReactStateStore, useUser }; 7 | -------------------------------------------------------------------------------- /packages/astro-auth-client/src/signIn/index.ts: -------------------------------------------------------------------------------- 1 | import { Providers } from "@astro-auth/types"; 2 | import { MetaMaskInpageProvider } from "@metamask/providers"; 3 | import { ethers } from "ethers"; 4 | 5 | declare global { 6 | interface Window { 7 | ethereum: MetaMaskInpageProvider; 8 | } 9 | } 10 | 11 | const signIn = async ({ 12 | provider, 13 | login, 14 | callbackURL, 15 | }: { 16 | provider: Providers; 17 | login?: any; 18 | callbackURL?: string; 19 | }) => { 20 | if (provider == "credential" && !login) { 21 | throw new Error( 22 | "ASTROAUTH: Login Details Are Required For The Credential Provider" 23 | ); 24 | } 25 | 26 | let metamaskInfo: { 27 | address?: string; 28 | signature?: string; 29 | } = {}; 30 | 31 | if (provider == "metamask") { 32 | if (!window.ethereum) { 33 | return { 34 | error: "Metamask is not installed", 35 | }; 36 | } 37 | 38 | await window.ethereum.request({ 39 | method: "eth_requestAccounts", 40 | }); 41 | const provider = new ethers.providers.Web3Provider(window.ethereum as any); 42 | const signer = provider.getSigner(); 43 | 44 | const response = await fetch("api/auth/sign-message"); 45 | const data = await response.json(); 46 | 47 | if (!response.ok) { 48 | return { 49 | error: data.error, 50 | }; 51 | } 52 | 53 | const signature = await signer.signMessage(data.message); 54 | const address = await signer.getAddress(); 55 | 56 | metamaskInfo = { 57 | address, 58 | signature, 59 | }; 60 | } 61 | 62 | const response = await fetch("api/auth/signin", { 63 | method: "POST", 64 | body: JSON.stringify({ 65 | provider, 66 | callback: callbackURL ?? location.href, 67 | login: login ?? metamaskInfo, 68 | }), 69 | }); 70 | 71 | const data = await response.json(); 72 | 73 | if (!response.ok) { 74 | return { 75 | error: data.error, 76 | }; 77 | } 78 | 79 | if (window.location) { 80 | location.href = data.loginURL || data.redirect; 81 | } 82 | return data; 83 | }; 84 | 85 | export default signIn; 86 | -------------------------------------------------------------------------------- /packages/astro-auth-client/src/signOut/index.ts: -------------------------------------------------------------------------------- 1 | import { Providers } from "@astro-auth/types"; 2 | import axios from "redaxios"; 3 | 4 | const signIn = async (callback?: string) => { 5 | const { data } = await axios.delete("/api/auth/signout"); 6 | 7 | if (window.location) { 8 | location.href = callback ?? "/"; 9 | } 10 | return data; 11 | }; 12 | 13 | export default signIn; 14 | -------------------------------------------------------------------------------- /packages/astro-auth-client/src/stateStores/ReactStateStore/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from "react"; 2 | import userAtom, { setUser } from "../store/userStore"; 3 | import { useStore } from "@nanostores/react"; 4 | import axios from "redaxios"; 5 | 6 | interface ReactStateStoreProps { 7 | user: any; 8 | } 9 | 10 | const ReactStateStore = ({ user }: ReactStateStoreProps) => { 11 | useEffect(() => { 12 | setUser(user); 13 | }, [user]); 14 | 15 | return null; 16 | }; 17 | 18 | ReactStateStore.useUser = (config?: { update?: boolean }) => { 19 | const user = useStore(userAtom); 20 | 21 | useEffect(() => { 22 | (async () => { 23 | if (config?.update) { 24 | const fetchedUser = await axios.get("/api/auth/user"); 25 | setUser(fetchedUser.data); 26 | } 27 | })(); 28 | }, []); 29 | 30 | return user; 31 | }; 32 | 33 | export default ReactStateStore; 34 | -------------------------------------------------------------------------------- /packages/astro-auth-client/src/stateStores/store/useUser.ts: -------------------------------------------------------------------------------- 1 | import axios from "redaxios"; 2 | 3 | const useUser = async () => { 4 | const user = await axios.get("/api/auth/user"); 5 | return user.data; 6 | }; 7 | 8 | export default useUser; 9 | -------------------------------------------------------------------------------- /packages/astro-auth-client/src/stateStores/store/userStore.ts: -------------------------------------------------------------------------------- 1 | import { atom } from "nanostores"; 2 | 3 | const userAtom = atom(null); 4 | export default userAtom; 5 | 6 | export const setUser = (newUser: any) => { 7 | userAtom.set(newUser); 8 | }; 9 | -------------------------------------------------------------------------------- /packages/astro-auth-client/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6", 4 | "esModuleInterop": true, 5 | "forceConsistentCasingInFileNames": true, 6 | "strict": true, 7 | "skipLibCheck": true, 8 | "jsx": "react", 9 | "module": "ESNext", 10 | "declaration": true, 11 | "declarationDir": "types", 12 | "sourceMap": true, 13 | "outDir": "dist", 14 | "moduleResolution": "node", 15 | "allowSyntheticDefaultImports": true, 16 | "emitDeclarationOnly": true 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /packages/astro-auth-core/README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 |

Astro Auth Core

4 | 5 |

6 | 7 |

8 | 9 | ## Getting Started 10 | 11 | Run one of the following command inside your project directory to install the package: 12 | 13 | ``` 14 | yarn add @astro-auth/core 15 | 16 | or 17 | 18 | npm i @astro-auth/core 19 | ``` 20 | 21 | ## Documentation 22 | 23 | Read the documentation at 24 | 25 | ## Contributing 26 | 27 | We're open to all community contributions, just make a pull request! 28 | -------------------------------------------------------------------------------- /packages/astro-auth-core/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@astro-auth/core", 3 | "version": "1.0.12", 4 | "description": "The core package of the Astro Auth library", 5 | "main": "dist/index.js", 6 | "type": "module", 7 | "author": "Osada Vidath ", 8 | "license": "MIT", 9 | "private": false, 10 | "files": [ 11 | "dist" 12 | ], 13 | "keywords": [ 14 | "astro-component", 15 | "astro-auth", 16 | "astro-auth-core" 17 | ], 18 | "module": "dist/index.js", 19 | "types": "dist/index.d.ts", 20 | "scripts": { 21 | "build": "rollup -c", 22 | "dev": "rollup -c -w" 23 | }, 24 | "devDependencies": { 25 | "@rollup/plugin-commonjs": "^21.0.3", 26 | "@rollup/plugin-json": "^4.1.0", 27 | "@rollup/plugin-node-resolve": "^13.2.0", 28 | "@rollup/plugin-typescript": "^8.3.1", 29 | "@types/jsonwebtoken": "^8.5.8", 30 | "astro": "^1.0.0-beta.20", 31 | "rollup": "^2.70.1", 32 | "rollup-plugin-dts": "^4.2.1", 33 | "rollup-plugin-peer-deps-external": "^2.2.4", 34 | "rollup-plugin-terser": "^7.0.2", 35 | "typescript": "^4.6.3" 36 | }, 37 | "dependencies": { 38 | "@astro-auth/types": "1.0.2", 39 | "ethers": "^5.6.6", 40 | "jsonwebtoken": "^8.5.1", 41 | "openid-client": "^5.1.5" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /packages/astro-auth-core/rollup.config.js: -------------------------------------------------------------------------------- 1 | import resolve from "@rollup/plugin-node-resolve"; 2 | import typescript from "@rollup/plugin-typescript"; 3 | import commonjs from "@rollup/plugin-commonjs"; 4 | import dts from "rollup-plugin-dts"; 5 | import { terser } from "rollup-plugin-terser"; 6 | import peerDepsExternal from "rollup-plugin-peer-deps-external"; 7 | import json from "@rollup/plugin-json"; 8 | 9 | const packageJson = require("./package.json"); 10 | 11 | export default [ 12 | { 13 | input: "src/index.ts", 14 | output: [ 15 | { 16 | file: packageJson.module, 17 | format: "esm", 18 | sourcemap: true, 19 | }, 20 | ], 21 | plugins: [ 22 | json(), 23 | peerDepsExternal(), 24 | resolve({ 25 | preferBuiltins: true, 26 | }), 27 | commonjs(), 28 | typescript({ tsconfig: "./tsconfig.json" }), 29 | terser(), 30 | ], 31 | }, 32 | { 33 | input: "dist/types/index.d.ts", 34 | output: [{ file: "dist/index.d.ts", format: "esm" }], 35 | plugins: [dts()], 36 | external: [/\.css$/], 37 | }, 38 | ]; 39 | -------------------------------------------------------------------------------- /packages/astro-auth-core/src/AstroAuth/handlers/getMessageToSign.ts: -------------------------------------------------------------------------------- 1 | const getMessageToSign = (message: string) => { 2 | return message ?? "Welcome To Astro Auth"; 3 | }; 4 | 5 | export default getMessageToSign; 6 | -------------------------------------------------------------------------------- /packages/astro-auth-core/src/AstroAuth/handlers/getServerUser.ts: -------------------------------------------------------------------------------- 1 | import parseCookie from "../../utils/parseCookieString"; 2 | import jwt from "jsonwebtoken"; 3 | 4 | const getServerUser = async (request: Request, user?: (user: any) => any) => { 5 | const cookies = parseCookie(request.headers.get("cookie") || ""); 6 | const session = cookies["__astroauth__session__"]; 7 | 8 | if (!session) { 9 | return { 10 | status: 401, 11 | body: { 12 | error: "Unauthorized", 13 | }, 14 | }; 15 | } 16 | 17 | const payload = jwt.verify(session, import.meta.env.ASTROAUTH_SECRET); 18 | 19 | const newPayload = user ? await user(payload) : payload; 20 | const newJWT = jwt.sign(newPayload, import.meta.env.ASTROAUTH_SECRET); 21 | 22 | return { 23 | status: 200, 24 | body: newPayload, 25 | headers: { 26 | "Set-Cookie": `__astroauth__session__=${newJWT}; HttpOnly; Path=/;`, 27 | }, 28 | }; 29 | }; 30 | 31 | export default getServerUser; 32 | -------------------------------------------------------------------------------- /packages/astro-auth-core/src/AstroAuth/handlers/index.ts: -------------------------------------------------------------------------------- 1 | import { MetamaskUserOptions } from "@astro-auth/types"; 2 | import { AstroAuthParams } from ".."; 3 | import getURLSlash from "../../utils/getURLSlash"; 4 | import parseCookie from "../../utils/parseCookieString"; 5 | import getMessageToSign from "./getMessageToSign"; 6 | import getServerUser from "./getServerUser"; 7 | import OAuthCallback from "./oauthCallback"; 8 | import signIn from "./signIn"; 9 | import signOut from "./signout"; 10 | 11 | const astroAuthHandler = async ( 12 | request: Request, 13 | url: string, 14 | config: AstroAuthParams 15 | ) => { 16 | const cookies = parseCookie(request.headers.get("cookie") || ""); 17 | const requestBody: { 18 | provider: string; 19 | callback: string; 20 | } = await request 21 | .clone() 22 | .json() 23 | .catch(() => {}); 24 | 25 | switch (url) { 26 | case "signin": { 27 | const authConfig = config.authProviders?.find( 28 | (provider) => provider.id === requestBody.provider 29 | ); 30 | 31 | return signIn( 32 | request, 33 | requestBody.callback, 34 | authConfig, 35 | config.hooks?.jwt, 36 | config.hooks?.redirectError 37 | ); 38 | } 39 | case "signout": { 40 | return signOut(request); 41 | } 42 | case "user": { 43 | return getServerUser(request, config.hooks?.account); 44 | } 45 | case "sign-message": { 46 | const metamaskProvider = config.authProviders?.find( 47 | (provider) => provider.id == "metamask" 48 | ); 49 | 50 | if (!metamaskProvider) { 51 | if (config.hooks?.redirectError) { 52 | const redirectURL = await config.hooks.redirectError( 53 | new Error("Metamask provider is not configured") 54 | ); 55 | 56 | return { 57 | status: 307, 58 | headers: { 59 | "Content-Type": undefined, 60 | Location: `${getURLSlash( 61 | redirectURL 62 | )}/?error=Metamask provider is not configured`, 63 | }, 64 | }; 65 | } 66 | 67 | throw new Error("Metamask is not configured"); 68 | } 69 | 70 | return { 71 | status: 200, 72 | body: { 73 | message: getMessageToSign( 74 | (metamaskProvider?.options as MetamaskUserOptions).signMessage 75 | ), 76 | }, 77 | }; 78 | } 79 | default: { 80 | if (url.startsWith("oauth")) { 81 | const oauthConfig = config.authProviders?.find( 82 | (provider) => provider.id === url.split("/")[1] 83 | ); 84 | const code = new URL(request.url).searchParams.get("code"); 85 | 86 | if ( 87 | oauthConfig?.type == "credential" || 88 | oauthConfig?.type == "metamask" 89 | ) { 90 | return { 91 | status: 500, 92 | }; 93 | } 94 | 95 | const { user, encodedJWT } = await OAuthCallback( 96 | request, 97 | oauthConfig, 98 | code ?? undefined, 99 | config.hooks?.jwt, 100 | config.hooks?.redirectError 101 | ); 102 | 103 | // This could be a boolean or a string 104 | const shouldUserLoginHookResponse = config.hooks?.signIn 105 | ? await config.hooks?.signIn(user) 106 | : null; 107 | 108 | const shouldUserLogin = config.hooks?.signIn 109 | ? typeof shouldUserLoginHookResponse == "boolean" 110 | ? !!shouldUserLoginHookResponse 111 | : false 112 | : true; 113 | 114 | if (!shouldUserLogin) { 115 | return { 116 | status: 302, 117 | headers: { 118 | Location: 119 | typeof shouldUserLoginHookResponse == "string" 120 | ? shouldUserLoginHookResponse 121 | : "/?error=Not Allowed", 122 | "Content-Type": undefined, 123 | "Set-Cookie": 124 | "__astroauth__session__=deleted; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT", 125 | }, 126 | }; 127 | } 128 | 129 | return { 130 | status: 307, 131 | headers: { 132 | "Set-Cookie": `__astroauth__session__=${encodedJWT}; HttpOnly; Path=/;`, 133 | "Content-Type": undefined, 134 | Location: cookies["__astroauth__callback__"] ?? "/", 135 | }, 136 | }; 137 | } 138 | } 139 | } 140 | }; 141 | 142 | export default astroAuthHandler; 143 | -------------------------------------------------------------------------------- /packages/astro-auth-core/src/AstroAuth/handlers/oauthCallback.ts: -------------------------------------------------------------------------------- 1 | import { OAuthConfig } from "@astro-auth/types"; 2 | import jwt from "jsonwebtoken"; 3 | 4 | import getUserDetails from "../../lib/oauth/getUserDetails"; 5 | import parseCookie from "../../utils/parseCookieString"; 6 | 7 | const OAuthCallback = async ( 8 | request: Request, 9 | oauthConfig?: OAuthConfig, 10 | code?: string, 11 | generateJWT?: (user: any) => any, 12 | redirectError?: ((error: Error) => Promise) | undefined 13 | ) => { 14 | if (request.method != "GET") { 15 | return { 16 | status: 405, 17 | body: { 18 | error: "Method not allowed", 19 | }, 20 | }; 21 | } 22 | 23 | if (!oauthConfig) { 24 | if (redirectError) { 25 | const redirectURL = await redirectError( 26 | new Error("Provider Is Not Configured") 27 | ); 28 | 29 | return { 30 | status: 307, 31 | headers: { 32 | "Content-Type": undefined, 33 | Location: `${redirectURL}/?error=Provider Is Not Configured`, 34 | }, 35 | }; 36 | } 37 | 38 | throw new Error("Provider Is Not Configured"); 39 | } 40 | 41 | if (!code) { 42 | if (redirectError) { 43 | const redirectURL = await redirectError( 44 | new Error(`${oauthConfig.name} OAuth Error`) 45 | ); 46 | 47 | return { 48 | status: 307, 49 | headers: { 50 | "Content-Type": undefined, 51 | Location: `${redirectURL}/?error=${oauthConfig.name} OAuth Error`, 52 | }, 53 | }; 54 | } 55 | 56 | throw new Error(`${oauthConfig.name} OAuth Error`); 57 | } 58 | 59 | try { 60 | const cookies = parseCookie(request.headers.get("cookie") ?? ""); 61 | 62 | const user = await getUserDetails(oauthConfig, code, cookies); 63 | 64 | const generatedData = generateJWT 65 | ? await generateJWT({ ...user, provider: oauthConfig.id }) 66 | : { 67 | accessToken: user.access_token, 68 | user: { 69 | ...user.user, 70 | originalUser: undefined, 71 | }, 72 | }; 73 | 74 | const encodedJWT = await jwt.sign( 75 | generatedData, 76 | import.meta.env.ASTROAUTH_SECRET 77 | ); 78 | 79 | return { user, encodedJWT }; 80 | } catch (error: any) { 81 | throw new Error(error.toString()); 82 | } 83 | }; 84 | 85 | export default OAuthCallback; 86 | -------------------------------------------------------------------------------- /packages/astro-auth-core/src/AstroAuth/handlers/signIn.ts: -------------------------------------------------------------------------------- 1 | import { 2 | OAuthConfig, 3 | CredentialConfig, 4 | MetamaskConfig, 5 | } from "@astro-auth/types"; 6 | import openIdClient from "../../lib/oauth/client"; 7 | import jwt from "jsonwebtoken"; 8 | import { getPKCE } from "../../lib/oauth/pkceUtils"; 9 | import { getState } from "../../lib/oauth/stateUtils"; 10 | import getURLSlash from "../../utils/getURLSlash"; 11 | import { ethers } from "ethers"; 12 | import getMessageToSign from "./getMessageToSign"; 13 | 14 | const signIn = async ( 15 | request: Request, 16 | callback: string, 17 | config?: OAuthConfig | CredentialConfig | MetamaskConfig, 18 | generateJWT?: (user: any) => any, 19 | redirectError?: ((error: Error) => Promise) | undefined 20 | ) => { 21 | if (request.method != "POST") { 22 | return { 23 | status: 405, 24 | body: { 25 | error: "Method not allowed", 26 | }, 27 | }; 28 | } 29 | 30 | if (config?.type == "credential") { 31 | const loginDetails = (await request.json().catch(() => {})).login; 32 | 33 | const user = await config.options.authorize(loginDetails); 34 | 35 | if (!user) { 36 | return { 37 | status: 401, 38 | headers: { 39 | "Content-Type": undefined, 40 | "Set-Cookie": 41 | "__astroauth__session__=deleted; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT", 42 | }, 43 | body: { 44 | redirect: "/?error=Wrong Credentials", 45 | }, 46 | }; 47 | } 48 | 49 | const generatedData = generateJWT 50 | ? await generateJWT({ ...user, provider: config.id }) 51 | : user; 52 | 53 | const encodedJWT = await jwt.sign( 54 | generatedData, 55 | import.meta.env.ASTROAUTH_SECRET 56 | ); 57 | 58 | return { 59 | status: 200, 60 | headers: { 61 | "Content-Type": undefined, 62 | "Set-Cookie": `__astroauth__session__=${encodedJWT}; path=/; HttpOnly; Path=/;`, 63 | }, 64 | body: { 65 | redirect: callback, 66 | }, 67 | }; 68 | } 69 | 70 | if (config?.type == "metamask") { 71 | const loginDetails = (await request.json().catch(() => {})).login; 72 | 73 | const signatureAddress = await ethers.utils.verifyMessage( 74 | getMessageToSign(config.options.signMessage), 75 | loginDetails.signature 76 | ); 77 | 78 | const user = await config.options.authorize(loginDetails); 79 | 80 | if (!user || signatureAddress != user.address) { 81 | return { 82 | status: 401, 83 | headers: { 84 | "Content-Type": undefined, 85 | "Set-Cookie": 86 | "__astroauth__session__=deleted; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT", 87 | }, 88 | body: { 89 | redirect: "/?error=Wrong Credentials", 90 | }, 91 | }; 92 | } 93 | 94 | const generatedData = generateJWT 95 | ? await generateJWT({ ...user, provider: config.id }) 96 | : user; 97 | 98 | const encodedJWT = await jwt.sign( 99 | generatedData, 100 | import.meta.env.ASTROAUTH_SECRET 101 | ); 102 | 103 | return { 104 | status: 200, 105 | headers: { 106 | "Content-Type": undefined, 107 | "Set-Cookie": `__astroauth__session__=${encodedJWT}; path=/; HttpOnly; Path=/;`, 108 | }, 109 | body: { 110 | redirect: callback, 111 | }, 112 | }; 113 | } 114 | 115 | if (!config) { 116 | if (redirectError) { 117 | const redirectURL = await redirectError( 118 | new Error("Provider Is Not Configured") 119 | ); 120 | 121 | return { 122 | status: 200, 123 | headers: { 124 | "Content-Type": undefined, 125 | }, 126 | body: { 127 | redirect: `${redirectURL}${getURLSlash( 128 | redirectURL 129 | )}?error=Provider Is Not Configured`, 130 | }, 131 | }; 132 | } 133 | throw new Error("Provider Is Not Configured"); 134 | } 135 | 136 | const oauthClient = await openIdClient(config); 137 | const pkceCode = getPKCE(config); 138 | const state = getState(config); 139 | 140 | const headers = new Headers(); 141 | headers.set( 142 | "Set-Cookie", 143 | `__astroauth__callback__=${callback}; HttpOnly; Path=/;` 144 | ); 145 | headers.set( 146 | "Set-Cookie", 147 | `__astroauth__state__=${state ?? ""}; HttpOnly; Path=/;` 148 | ); 149 | headers.set( 150 | "Set-Cookie", 151 | `__astroauth__pkce__=${pkceCode?.code_verifier ?? ""}; HttpOnly; Path=/;` 152 | ); 153 | 154 | return { 155 | status: 200, 156 | body: { 157 | loginURL: oauthClient.authorizationUrl({ 158 | state: state ?? undefined, 159 | scope: config.options.scope ? config.options.scope : config.scope, 160 | code_challenge: pkceCode?.code_challenge, 161 | code_challenge_method: pkceCode?.code_challenge_method, 162 | }), 163 | }, 164 | headers: { 165 | "Set-Cookie": `__astroauth__callback__=${callback}; HttpOnly; Path=/;`, 166 | // @ts-expect-error 167 | "Set-Cookie": `__astroauth__state__=${state ?? ""}; HttpOnly; Path=/;`, 168 | // @ts-expect-error 169 | "Set-Cookie": `__astroauth__pkce__=${ 170 | pkceCode?.code_verifier ?? "" 171 | }; HttpOnly; Path=/;`, 172 | }, 173 | }; 174 | }; 175 | 176 | export default signIn; 177 | -------------------------------------------------------------------------------- /packages/astro-auth-core/src/AstroAuth/handlers/signout.ts: -------------------------------------------------------------------------------- 1 | const signOut = async (request: Request) => { 2 | if (request.method != "DELETE") { 3 | return { 4 | status: 405, 5 | body: { 6 | error: "Method not allowed", 7 | }, 8 | }; 9 | } 10 | 11 | return { 12 | status: 200, 13 | headers: { 14 | "Set-Cookie": 15 | "__astroauth__session__=deleted; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT", 16 | }, 17 | }; 18 | }; 19 | 20 | export default signOut; 21 | -------------------------------------------------------------------------------- /packages/astro-auth-core/src/AstroAuth/index.ts: -------------------------------------------------------------------------------- 1 | import { 2 | OAuthConfig, 3 | CredentialConfig, 4 | MetamaskConfig, 5 | } from "@astro-auth/types"; 6 | import astroAuthHandler from "./handlers/index"; 7 | 8 | export interface AstroAuthParams { 9 | authProviders?: (OAuthConfig | CredentialConfig | MetamaskConfig)[]; 10 | hooks?: { 11 | jwt?: (user: any) => Promise; 12 | signIn?: (user: any) => Promise; 13 | redirectError?: (error: Error) => Promise; 14 | account?: (user: any) => Promise; 15 | }; 16 | } 17 | 18 | interface AuthHandlerResponse { 19 | status?: number; 20 | body?: any; 21 | headers?: { 22 | [key: string]: string | undefined | string[]; 23 | }; 24 | } 25 | 26 | const AstroAuth = (astroAuthParams: AstroAuthParams) => { 27 | return async ({ 28 | params: { astroauth }, 29 | request, 30 | }: { 31 | params: { 32 | astroauth: string; 33 | }; 34 | request: Request; 35 | }) => { 36 | const response: AuthHandlerResponse = (await astroAuthHandler( 37 | request, 38 | astroauth, 39 | astroAuthParams 40 | )) ?? { 41 | status: 500, 42 | body: { 43 | error: "Internal Server Error", 44 | }, 45 | }; 46 | 47 | if (!import.meta.env.ASTROAUTH_SECRET || !import.meta.env.ASTROAUTH_URL) { 48 | throw new Error("ASTROAUTH_SECRET and ASTROAUTH_URL must be set"); 49 | } 50 | 51 | return new Response(JSON.stringify(response?.body), { 52 | status: response?.status || 200, 53 | headers: { 54 | "Content-Type": "application/json", 55 | ...response?.headers, 56 | }, 57 | }); 58 | }; 59 | }; 60 | 61 | export default AstroAuth; 62 | -------------------------------------------------------------------------------- /packages/astro-auth-core/src/env.d.ts: -------------------------------------------------------------------------------- 1 | interface ImportMetaEnv { 2 | readonly ASTROAUTH_URL: string; 3 | readonly ASTROAUTH_SECRET: string; 4 | } 5 | 6 | interface ImportMeta { 7 | readonly env: ImportMetaEnv; 8 | } 9 | -------------------------------------------------------------------------------- /packages/astro-auth-core/src/getError/index.ts: -------------------------------------------------------------------------------- 1 | import { AstroGlobal } from "astro"; 2 | 3 | const getError = (astro: AstroGlobal) => { 4 | const error = new URL(astro.request.url).searchParams.get("error"); 5 | return error; 6 | }; 7 | 8 | export default getError; 9 | -------------------------------------------------------------------------------- /packages/astro-auth-core/src/getUser/index.ts: -------------------------------------------------------------------------------- 1 | import { AstroGlobal } from "astro"; 2 | import parseCookie from "../utils/parseCookieString"; 3 | import jwt from "jsonwebtoken"; 4 | 5 | const getUser = ({ 6 | client, 7 | server, 8 | }: { 9 | client?: AstroGlobal; 10 | server?: Request; 11 | }) => { 12 | if (client) { 13 | try { 14 | const sessionCookie = parseCookie( 15 | client.request.headers.get("cookie") ?? "" 16 | )["__astroauth__session__"]; 17 | 18 | if (!sessionCookie) { 19 | return null; 20 | } 21 | 22 | const decodedData = jwt.verify( 23 | sessionCookie, 24 | import.meta.env.ASTROAUTH_SECRET 25 | ); 26 | 27 | return decodedData; 28 | } catch (error: any) { 29 | return null; 30 | } 31 | } else if (server) { 32 | try { 33 | const sessionCookie = parseCookie(server.headers.get("cookie") ?? "")[ 34 | "__astroauth__session__" 35 | ]; 36 | 37 | if (!sessionCookie) { 38 | return null; 39 | } 40 | 41 | const decodedData = jwt.verify( 42 | sessionCookie, 43 | import.meta.env.ASTROAUTH_SECRET 44 | ); 45 | 46 | return decodedData; 47 | } catch (error: any) { 48 | return null; 49 | } 50 | } 51 | }; 52 | 53 | export default getUser; 54 | 55 | export const redirectUser = (loginPage: string) => { 56 | return new Response(null, { 57 | status: 302, 58 | headers: { 59 | Location: loginPage.startsWith("/") ? loginPage : `/${loginPage}`, 60 | }, 61 | }); 62 | }; 63 | -------------------------------------------------------------------------------- /packages/astro-auth-core/src/index.ts: -------------------------------------------------------------------------------- 1 | import AstroAuth from "./AstroAuth/index"; 2 | import getUser, { redirectUser } from "./getUser"; 3 | import getError from "./getError"; 4 | 5 | export default AstroAuth; 6 | export { getUser, getError, redirectUser }; 7 | export { OAuthConfig, OAuthUserOptions } from "@astro-auth/types"; 8 | -------------------------------------------------------------------------------- /packages/astro-auth-core/src/lib/oauth/client.ts: -------------------------------------------------------------------------------- 1 | import { Issuer, Client } from "openid-client"; 2 | import { OAuthConfig } from "@astro-auth/types"; 3 | 4 | const astroAuthURL = import.meta.env.ASTROAUTH_URL; 5 | 6 | const openIdClient = async (config: OAuthConfig) => { 7 | let issuer: Issuer; 8 | 9 | if (config.wellKnown) { 10 | issuer = await Issuer.discover(config.wellKnown); 11 | } else { 12 | issuer = new Issuer({ 13 | issuer: config.id, 14 | authorization_endpoint: config.authorization, 15 | token_endpoint: config.token, 16 | userinfo_endpoint: config.userinfo, 17 | }); 18 | } 19 | 20 | const client = new issuer.Client({ 21 | client_id: config.options.clientId, 22 | client_secret: config.options.clientSecret, 23 | redirect_uris: [`${astroAuthURL}/api/auth/oauth/${config.id}`], 24 | }); 25 | return client; 26 | }; 27 | 28 | export default openIdClient; 29 | -------------------------------------------------------------------------------- /packages/astro-auth-core/src/lib/oauth/getUserDetails.ts: -------------------------------------------------------------------------------- 1 | import { OAuthConfig } from "@astro-auth/types"; 2 | import openIdClient from "./client"; 3 | import { TokenSet } from "openid-client"; 4 | 5 | const astroAuthURL = import.meta.env.ASTROAUTH_URL; 6 | 7 | const getUserDetails = async ( 8 | oauthConfig: OAuthConfig, 9 | code: string, 10 | cookies: { 11 | [key: string]: string; 12 | } 13 | ) => { 14 | const oauthClient = await openIdClient(oauthConfig); 15 | let userTokens: TokenSet; 16 | 17 | if (oauthConfig.idToken) { 18 | userTokens = await oauthClient.callback( 19 | `${astroAuthURL}/api/auth/oauth/${oauthConfig.id}`, 20 | { code: code }, 21 | { 22 | code_verifier: cookies["__astroauth__pkce__"], 23 | state: cookies["__astroauth__state__"], 24 | } 25 | ); 26 | } else { 27 | userTokens = await oauthClient.oauthCallback( 28 | `${astroAuthURL}/api/auth/oauth/${oauthConfig.id}`, 29 | { code: code }, 30 | { 31 | code_verifier: cookies["__astroauth__pkce__"], 32 | state: cookies["__astroauth__state__"], 33 | } 34 | ); 35 | } 36 | 37 | const user = await oauthClient.userinfo(userTokens.access_token ?? "", { 38 | params: oauthConfig.userInfoParams, 39 | }); 40 | 41 | const transformedUser = { 42 | user: oauthConfig.profile(user), 43 | ...userTokens, 44 | }; 45 | 46 | return transformedUser; 47 | }; 48 | 49 | export default getUserDetails; 50 | -------------------------------------------------------------------------------- /packages/astro-auth-core/src/lib/oauth/pkceUtils.ts: -------------------------------------------------------------------------------- 1 | import { OAuthConfig } from "@astro-auth/types"; 2 | import { generators } from "openid-client"; 3 | 4 | export const getPKCE = (config: OAuthConfig) => { 5 | if (!config.checks?.includes("pkce")) { 6 | return null; 7 | } 8 | 9 | const code_verifier = generators.codeVerifier(); 10 | const code_challenge = generators.codeChallenge(code_verifier); 11 | 12 | return { 13 | code_challenge, 14 | // Code verifier should include in the cookie 15 | code_verifier, 16 | code_challenge_method: "S256", 17 | }; 18 | }; 19 | -------------------------------------------------------------------------------- /packages/astro-auth-core/src/lib/oauth/stateUtils.ts: -------------------------------------------------------------------------------- 1 | import { OAuthConfig } from "@astro-auth/types"; 2 | import { generators } from "openid-client"; 3 | 4 | export const getState = (config: OAuthConfig) => { 5 | if (!config.checks?.includes("state")) { 6 | return null; 7 | } 8 | 9 | const state = generators.state(); 10 | 11 | return state; 12 | }; 13 | -------------------------------------------------------------------------------- /packages/astro-auth-core/src/utils/getURLSlash.ts: -------------------------------------------------------------------------------- 1 | const getURLSlash = (url: string) => { 2 | return url.split("").pop() == "/" ? "" : "/"; 3 | }; 4 | 5 | export default getURLSlash; 6 | -------------------------------------------------------------------------------- /packages/astro-auth-core/src/utils/parseCookieString.ts: -------------------------------------------------------------------------------- 1 | const parseCookie = (str: string) => { 2 | if (!str) return {}; 3 | return str 4 | .split(";") 5 | .map((v) => v.split("=")) 6 | .reduce( 7 | ( 8 | acc: { 9 | [key: string]: string; 10 | }, 11 | v 12 | ) => { 13 | acc[decodeURIComponent(v[0].trim())] = decodeURIComponent(v[1].trim()); 14 | return acc; 15 | }, 16 | {} 17 | ); 18 | }; 19 | 20 | export default parseCookie; 21 | -------------------------------------------------------------------------------- /packages/astro-auth-core/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6", 4 | "module": "es2020", 5 | "esModuleInterop": true, 6 | "forceConsistentCasingInFileNames": true, 7 | "strict": true, 8 | "declaration": true, 9 | "declarationDir": "types", 10 | "sourceMap": true, 11 | "outDir": "dist", 12 | "skipLibCheck": true, 13 | "moduleResolution": "node" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /packages/astro-auth-providers/README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 |

Astro Auth Providers

4 | 5 |

6 | 7 |

8 | 9 | ## Getting Started 10 | 11 | Run one of the following command inside your project directory to install the package: 12 | 13 | ``` 14 | yarn add @astro-auth/providers 15 | 16 | or 17 | 18 | npm i @astro-auth/providers 19 | ``` 20 | 21 | ## Documentation 22 | 23 | Read the documentation at 24 | 25 | ## Contributing 26 | 27 | We're open to all community contributions, just make a pull request! 28 | -------------------------------------------------------------------------------- /packages/astro-auth-providers/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@astro-auth/providers", 3 | "version": "1.0.6", 4 | "main": "dist/index.js", 5 | "type": "module", 6 | "description": "The providers package of the Astro Auth library", 7 | "author": "Osada Vidath ", 8 | "license": "MIT", 9 | "files": [ 10 | "dist" 11 | ], 12 | "keywords": [ 13 | "astro-component", 14 | "astro-auth", 15 | "astro-auth-providers" 16 | ], 17 | "scripts": { 18 | "build": "tsc", 19 | "dev": "tsc -w" 20 | }, 21 | "devDependencies": { 22 | "@types/qs": "^6.9.7", 23 | "typescript": "^4.6.3" 24 | }, 25 | "dependencies": { 26 | "qs": "^6.10.3", 27 | "@astro-auth/types": "1.0.2" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /packages/astro-auth-providers/src/env.d.ts: -------------------------------------------------------------------------------- 1 | interface ImportMetaEnv { 2 | readonly ASTROAUTH_URL: string; 3 | readonly ASTROAUTH_SECRET: string; 4 | } 5 | 6 | interface ImportMeta { 7 | readonly env: ImportMetaEnv; 8 | } 9 | -------------------------------------------------------------------------------- /packages/astro-auth-providers/src/index.ts: -------------------------------------------------------------------------------- 1 | import GoogleProvider from "./providers/GoogleProvider"; 2 | import DiscordProvider from "./providers/DiscordProvider"; 3 | import TwitterProvider from "./providers/TwitterProvider"; 4 | import FacebookProvider from "./providers/FacebookProvider"; 5 | import GithubProvider from "./providers/GithubProvider"; 6 | import InstagramProvider from "./providers/InstagramProvider"; 7 | import SpotifyProvider from "./providers/SpotifyProvider"; 8 | import ZoomProvider from "./providers/ZoomProvider"; 9 | 10 | import CredentialProvider from "./providers/CredentialProvider"; 11 | import MetamaskProvider from "./providers/MetamaskProvider"; 12 | 13 | export { 14 | GoogleProvider, 15 | DiscordProvider, 16 | TwitterProvider, 17 | FacebookProvider, 18 | GithubProvider, 19 | InstagramProvider, 20 | SpotifyProvider, 21 | ZoomProvider, 22 | CredentialProvider, 23 | MetamaskProvider, 24 | }; 25 | -------------------------------------------------------------------------------- /packages/astro-auth-providers/src/providers/CredentialProvider.ts: -------------------------------------------------------------------------------- 1 | import { CredentialUserOptions, CredentialConfig } from "@astro-auth/types"; 2 | 3 | const CredentialProvider = ( 4 | options: CredentialUserOptions 5 | ): CredentialConfig => { 6 | return { 7 | id: "credential", 8 | type: "credential", 9 | options, 10 | }; 11 | }; 12 | 13 | export default CredentialProvider; 14 | -------------------------------------------------------------------------------- /packages/astro-auth-providers/src/providers/DiscordProvider.ts: -------------------------------------------------------------------------------- 1 | import { OAuthConfig, OAuthUserOptions } from "@astro-auth/types"; 2 | 3 | const DiscordProvider = (options: OAuthUserOptions): OAuthConfig => { 4 | return { 5 | id: "discord", 6 | name: "Discord", 7 | type: "oauth", 8 | scope: "identify email", 9 | options, 10 | authorization: "https://discord.com/api/oauth2/authorize", 11 | token: "https://discord.com/api/oauth2/token", 12 | userinfo: "https://discord.com/api/users/@me", 13 | checks: ["state"], 14 | profile(profile) { 15 | if (profile.avatar === null) { 16 | const defaultAvatarNumber = parseInt(profile.discriminator) % 5; 17 | profile.image_url = `https://cdn.discordapp.com/embed/avatars/${defaultAvatarNumber}.png`; 18 | } else { 19 | const format = profile.avatar.startsWith("a_") ? "gif" : "png"; 20 | profile.image_url = `https://cdn.discordapp.com/avatars/${profile.id}/${profile.avatar}.${format}`; 21 | } 22 | return { 23 | id: profile.id, 24 | name: profile.username, 25 | email: profile.email, 26 | image: profile.image_url, 27 | originalUser: { ...profile }, 28 | }; 29 | }, 30 | }; 31 | }; 32 | 33 | export default DiscordProvider; 34 | -------------------------------------------------------------------------------- /packages/astro-auth-providers/src/providers/FacebookProvider.ts: -------------------------------------------------------------------------------- 1 | import { OAuthConfig, OAuthUserOptions } from "@astro-auth/types"; 2 | 3 | const FacebookProvider = (options: OAuthUserOptions): OAuthConfig => { 4 | return { 5 | id: "facebook", 6 | name: "Facebook", 7 | type: "oauth", 8 | scope: "email", 9 | options, 10 | authorization: "https://www.facebook.com/v11.0/dialog/oauth", 11 | token: "https://graph.facebook.com/oauth/access_token", 12 | userinfo: "https://api.github.com/user", 13 | profile(profile) { 14 | return { 15 | id: profile.id.toString(), 16 | name: profile.name || profile.login, 17 | email: profile.email, 18 | image: profile.avatar_url, 19 | originalUser: { ...profile }, 20 | }; 21 | }, 22 | }; 23 | }; 24 | 25 | export default FacebookProvider; 26 | -------------------------------------------------------------------------------- /packages/astro-auth-providers/src/providers/GithubProvider.ts: -------------------------------------------------------------------------------- 1 | import { OAuthConfig, OAuthUserOptions } from "@astro-auth/types"; 2 | 3 | const GithubProvider = (options: OAuthUserOptions): OAuthConfig => { 4 | return { 5 | id: "github", 6 | name: "GitHub", 7 | type: "oauth", 8 | scope: "read:user user:email", 9 | options, 10 | authorization: "https://github.com/login/oauth/authorize", 11 | token: "https://github.com/login/oauth/access_token", 12 | userinfo: "https://api.github.com/user", 13 | profile(profile) { 14 | return { 15 | id: profile.id.toString(), 16 | name: profile.name || profile.login, 17 | email: profile.email, 18 | image: profile.avatar_url, 19 | }; 20 | }, 21 | }; 22 | }; 23 | 24 | export default GithubProvider; 25 | -------------------------------------------------------------------------------- /packages/astro-auth-providers/src/providers/GoogleProvider.ts: -------------------------------------------------------------------------------- 1 | import { OAuthConfig, OAuthUserOptions } from "@astro-auth/types"; 2 | 3 | const GoogleProvider = (options: OAuthUserOptions): OAuthConfig => { 4 | return { 5 | id: "google", 6 | name: "Google", 7 | type: "oauth", 8 | scope: "openid email profile", 9 | options, 10 | wellKnown: "https://accounts.google.com/.well-known/openid-configuration", 11 | idToken: true, 12 | checks: ["state", "pkce"], 13 | profile(profile) { 14 | return { 15 | id: profile.sub, 16 | name: profile.name, 17 | email: profile.email, 18 | image: profile.picture, 19 | originalUser: { ...profile }, 20 | }; 21 | }, 22 | }; 23 | }; 24 | 25 | export default GoogleProvider; 26 | -------------------------------------------------------------------------------- /packages/astro-auth-providers/src/providers/InstagramProvider.ts: -------------------------------------------------------------------------------- 1 | import { OAuthConfig, OAuthUserOptions } from "@astro-auth/types"; 2 | 3 | const InstagramProvider = (options: OAuthUserOptions): OAuthConfig => { 4 | return { 5 | id: "instagram", 6 | name: "Instagram", 7 | type: "oauth", 8 | scope: "user_profile", 9 | options, 10 | authorization: "https://api.instagram.com/oauth/authorize", 11 | token: "https://api.instagram.com/oauth/access_token", 12 | userinfo: 13 | "https://graph.instagram.com/me?fields=id,username,account_type,name", 14 | profile(profile) { 15 | return { 16 | id: profile.id, 17 | name: profile.username, 18 | email: null, 19 | image: null, 20 | originalUser: { ...profile }, 21 | }; 22 | }, 23 | }; 24 | }; 25 | 26 | export default InstagramProvider; 27 | -------------------------------------------------------------------------------- /packages/astro-auth-providers/src/providers/MetamaskProvider.ts: -------------------------------------------------------------------------------- 1 | import { MetamaskConfig, MetamaskUserOptions } from "@astro-auth/types"; 2 | 3 | const CredentialProvider = (options: MetamaskUserOptions): MetamaskConfig => { 4 | return { 5 | id: "metamask", 6 | type: "metamask", 7 | options, 8 | }; 9 | }; 10 | 11 | export default CredentialProvider; 12 | -------------------------------------------------------------------------------- /packages/astro-auth-providers/src/providers/SpotifyProvider.ts: -------------------------------------------------------------------------------- 1 | import { OAuthConfig, OAuthUserOptions } from "@astro-auth/types"; 2 | 3 | const SpotifyProvider = (options: OAuthUserOptions): OAuthConfig => { 4 | return { 5 | id: "spotify", 6 | name: "Spotify", 7 | type: "oauth", 8 | scope: "user-read-email", 9 | options, 10 | authorization: "https://accounts.spotify.com/authorize", 11 | token: "https://accounts.spotify.com/api/token", 12 | userinfo: "https://api.spotify.com/v1/me", 13 | profile(profile) { 14 | return { 15 | id: profile.id, 16 | name: profile.display_name, 17 | email: profile.email, 18 | image: profile.images?.[0]?.url, 19 | originalUser: { ...profile }, 20 | }; 21 | }, 22 | }; 23 | }; 24 | 25 | export default SpotifyProvider; 26 | -------------------------------------------------------------------------------- /packages/astro-auth-providers/src/providers/TwitterProvider.ts: -------------------------------------------------------------------------------- 1 | import { OAuthConfig, OAuthUserOptions } from "@astro-auth/types"; 2 | 3 | const TwitterProvider = (options: OAuthUserOptions): OAuthConfig => { 4 | return { 5 | id: "twitter", 6 | name: "Twitter", 7 | type: "oauth", 8 | scope: "users.read tweet.read offline.access", 9 | options, 10 | authorization: "https://twitter.com/i/oauth2/authorize", 11 | token: "https://api.twitter.com/2/oauth2/token", 12 | userinfo: "https://api.twitter.com/2/users/me", 13 | userInfoParams: { "user.fields": "profile_image_url" }, 14 | profile({ data: profile }) { 15 | return { 16 | id: profile.id, 17 | name: profile.name, 18 | // E-mail is currently unsupported by OAuth 2 Twitter. 19 | email: null, 20 | image: profile.profile_image_url.replace("normal", "400x400"), 21 | originalUser: { ...profile }, 22 | }; 23 | }, 24 | checks: ["pkce", "state"], 25 | }; 26 | }; 27 | 28 | export default TwitterProvider; 29 | -------------------------------------------------------------------------------- /packages/astro-auth-providers/src/providers/ZoomProvider.ts: -------------------------------------------------------------------------------- 1 | import { OAuthConfig, OAuthUserOptions } from "@astro-auth/types"; 2 | 3 | const ZoomProvider = (options: OAuthUserOptions): OAuthConfig => { 4 | return { 5 | id: "zoom", 6 | name: "Zoom", 7 | type: "oauth", 8 | scope: "user:read", 9 | options, 10 | authorization: "https://zoom.us/oauth/authorize?scope", 11 | token: "https://zoom.us/oauth/token", 12 | userinfo: "https://api.zoom.us/v2/users/me", 13 | profile(profile) { 14 | return { 15 | id: profile.id, 16 | name: `${profile.first_name} ${profile.last_name}`, 17 | email: profile.email, 18 | image: profile.pic_url, 19 | originalUser: { ...profile }, 20 | }; 21 | }, 22 | }; 23 | }; 24 | 25 | export default ZoomProvider; 26 | -------------------------------------------------------------------------------- /packages/astro-auth-providers/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6", 4 | "module": "ES2020", 5 | "esModuleInterop": true, 6 | "forceConsistentCasingInFileNames": true, 7 | "strict": true, 8 | "declaration": true, 9 | "skipLibCheck": true, 10 | "outDir": "dist", 11 | "rootDir": "src", 12 | "moduleResolution": "node" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /packages/astro-auth-types/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@astro-auth/types", 3 | "version": "1.0.2", 4 | "main": "src/index.ts", 5 | "author": "Osada Vidath ", 6 | "license": "MIT", 7 | "files": [ 8 | "src" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /packages/astro-auth-types/src/index.ts: -------------------------------------------------------------------------------- 1 | export interface OAuthUserOptions { 2 | clientId: string; 3 | clientSecret: string; 4 | scope?: string; 5 | } 6 | 7 | export interface OAuthConfig { 8 | id: string; 9 | name: string; 10 | type: "oauth"; 11 | options: OAuthUserOptions; 12 | scope?: string; 13 | wellKnown?: string; 14 | idToken?: boolean; 15 | checks?: string[]; 16 | authorization?: string; 17 | token?: string; 18 | userinfo?: string; 19 | userInfoParams?: object; 20 | profile: (profile: any) => any; 21 | } 22 | 23 | export interface CredentialUserOptions { 24 | authorize: (properties: any) => Promise; 25 | } 26 | 27 | export interface CredentialConfig { 28 | id: "credential"; 29 | type: "credential"; 30 | options: CredentialUserOptions; 31 | } 32 | 33 | export interface MetamaskUserOptions { 34 | signMessage: string; 35 | authorize: (properties: any) => Promise; 36 | } 37 | 38 | export interface MetamaskConfig { 39 | id: "metamask"; 40 | type: "metamask"; 41 | options: MetamaskUserOptions; 42 | } 43 | 44 | export type Providers = 45 | | "credential" 46 | | "metamask" 47 | | "google" 48 | | "discord" 49 | | "twitter" 50 | | "facebook" 51 | | "github" 52 | | "instagram" 53 | | "spotify" 54 | | "zoom"; 55 | -------------------------------------------------------------------------------- /packages/astro-auth-ui/README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 |

Astro Auth UI

4 | 5 |

6 | 7 |

8 | 9 | ## Getting Started 10 | 11 | Run one of the following command inside your project directory to install the package: 12 | 13 | ``` 14 | yarn add @astro-auth/ui 15 | 16 | or 17 | 18 | npm i @astro-auth/ui 19 | ``` 20 | 21 | ## Documentation 22 | 23 | Read the documentation at 24 | 25 | ## Contributing 26 | 27 | We're open to all community contributions, just make a pull request! 28 | -------------------------------------------------------------------------------- /packages/astro-auth-ui/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./src"; 2 | -------------------------------------------------------------------------------- /packages/astro-auth-ui/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@astro-auth/ui", 3 | "version": "1.0.5", 4 | "license": "MIT", 5 | "type": "module", 6 | "description": "The UI package of the Astro Auth library", 7 | "author": "Osada Vidath ", 8 | "keywords": [ 9 | "astro-component", 10 | "astro-auth", 11 | "astro-auth-ui", 12 | "ui" 13 | ], 14 | "devDependencies": { 15 | "@types/react": "^18.0.1", 16 | "@types/react-dom": "^18.0.0" 17 | }, 18 | "dependencies": { 19 | "@astro-auth/client": "1.0.5" 20 | }, 21 | "peerDependencies": { 22 | "react": "^18.0.0", 23 | "react-dom": "^18.0.0" 24 | }, 25 | "files": [ 26 | "src", 27 | "index.ts" 28 | ], 29 | "exports": { 30 | ".": "./index.ts" 31 | } 32 | } -------------------------------------------------------------------------------- /packages/astro-auth-ui/src/components/Button/index.module.css: -------------------------------------------------------------------------------- 1 | .button { 2 | border: none; 3 | background: none; 4 | font-family: "RTAliasMedium", monospace; 5 | position: relative; 6 | --border-radius: 8; 7 | --pixel-size: 4; 8 | border-radius: calc(var(--border-radius) * 1px); 9 | padding: 0.67rem 2.5rem; 10 | cursor: pointer; 11 | color: #fff; 12 | font-size: 1.3rem; 13 | display: flex; 14 | justify-content: center; 15 | align-items: center; 16 | padding-top: 1rem; 17 | transition: 0.2s transform, 0.2s background; 18 | 19 | --link-color-stop-a: #1d5bfc; 20 | --link-color-stop-b: #c238bd; 21 | } 22 | 23 | .button:hover { 24 | transform: translateY(-3px); 25 | 26 | --link-color-stop-a: #6d39ff; 27 | --link-color-stop-b: #af43ff; 28 | } 29 | 30 | .button:active { 31 | transform: translateY(0); 32 | 33 | --link-color-stop-a: #1d5bfc; 34 | --link-color-stop-b: #c238bd; 35 | } 36 | 37 | .button::before { 38 | content: ""; 39 | position: absolute; 40 | top: calc(var(--pixel-size) * 1px); 41 | right: 0; 42 | bottom: calc(var(--pixel-size) * 1px); 43 | left: 0; 44 | background: linear-gradient( 45 | 180deg, 46 | var(--link-color-stop-a), 47 | var(--link-color-stop-b) 48 | ); 49 | z-index: -1; 50 | } 51 | 52 | .button::after { 53 | content: ""; 54 | position: absolute; 55 | top: 0; 56 | right: calc(var(--pixel-size) * 1px); 57 | bottom: 0; 58 | left: calc(var(--pixel-size) * 1px); 59 | background: linear-gradient( 60 | 180deg, 61 | var(--link-color-stop-a), 62 | var(--link-color-stop-b) 63 | ); 64 | z-index: -1; 65 | } 66 | 67 | /* .button::after { 68 | content: ""; 69 | display: block; 70 | position: absolute; 71 | top: 4px; 72 | bottom: 4px; 73 | left: -6px; 74 | right: -6px; 75 | background: linear-gradient(180deg, #1d5bfc, #c238bd); 76 | z-index: -1; 77 | } */ 78 | -------------------------------------------------------------------------------- /packages/astro-auth-ui/src/components/Button/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC } from "react"; 2 | import styles from "./index.module.css"; 3 | 4 | interface ButtonProps { 5 | children: string; 6 | onClick?: () => void; 7 | } 8 | 9 | const Button: FC = ({ children, onClick }) => { 10 | return ( 11 | 14 | ); 15 | }; 16 | 17 | export default Button; 18 | -------------------------------------------------------------------------------- /packages/astro-auth-ui/src/components/DiscordButton/index.tsx: -------------------------------------------------------------------------------- 1 | import { signIn } from "@astro-auth/client"; 2 | import React, { FC, useEffect } from "react"; 3 | import styles from "../socialButtons.module.css"; 4 | 5 | interface DiscordButtonProps { 6 | children?: string; 7 | onClick?: () => void; 8 | callbackURL?: string; 9 | } 10 | 11 | const DiscordButton: FC = ({ 12 | children = "Login With Discord", 13 | callbackURL, 14 | onClick = () => 15 | signIn({ 16 | provider: "discord", 17 | callbackURL: callbackURL, 18 | }), 19 | }) => { 20 | return ( 21 | 30 | ); 31 | }; 32 | 33 | export default DiscordButton; 34 | -------------------------------------------------------------------------------- /packages/astro-auth-ui/src/components/FacebookButton/index.tsx: -------------------------------------------------------------------------------- 1 | import { signIn } from "@astro-auth/client"; 2 | import React, { FC, useEffect } from "react"; 3 | import styles from "../socialButtons.module.css"; 4 | 5 | interface FacebookButtonProps { 6 | children?: string; 7 | onClick?: () => void; 8 | callbackURL?: string; 9 | } 10 | 11 | const FacebookButton: FC = ({ 12 | children = "Login With Facebook", 13 | callbackURL, 14 | onClick = () => 15 | signIn({ 16 | provider: "facebook", 17 | callbackURL: callbackURL, 18 | }), 19 | }) => { 20 | return ( 21 | 30 | ); 31 | }; 32 | 33 | export default FacebookButton; 34 | -------------------------------------------------------------------------------- /packages/astro-auth-ui/src/components/GithubButton/index.tsx: -------------------------------------------------------------------------------- 1 | import { signIn } from "@astro-auth/client"; 2 | import React, { FC, useEffect } from "react"; 3 | import styles from "../socialButtons.module.css"; 4 | 5 | interface GithubButtonProps { 6 | children?: string; 7 | onClick?: () => void; 8 | callbackURL?: string; 9 | } 10 | 11 | const GithubButton: FC = ({ 12 | children = "Login With Github", 13 | callbackURL, 14 | onClick = () => 15 | signIn({ 16 | provider: "github", 17 | callbackURL: callbackURL, 18 | }), 19 | }) => { 20 | return ( 21 | 30 | ); 31 | }; 32 | 33 | export default GithubButton; 34 | -------------------------------------------------------------------------------- /packages/astro-auth-ui/src/components/GoogleButton/index.tsx: -------------------------------------------------------------------------------- 1 | import { signIn } from "@astro-auth/client"; 2 | import React, { FC, useEffect } from "react"; 3 | import styles from "../socialButtons.module.css"; 4 | 5 | interface GoogleButtonProps { 6 | children?: string; 7 | onClick?: () => void; 8 | callbackURL?: string; 9 | } 10 | 11 | const GoogleButton: FC = ({ 12 | children = "Login With Google", 13 | callbackURL, 14 | onClick = () => 15 | signIn({ 16 | provider: "google", 17 | callbackURL: callbackURL, 18 | }), 19 | }) => { 20 | return ( 21 | 30 | ); 31 | }; 32 | 33 | export default GoogleButton; 34 | -------------------------------------------------------------------------------- /packages/astro-auth-ui/src/components/InstagramButton/index.tsx: -------------------------------------------------------------------------------- 1 | import { signIn } from "@astro-auth/client"; 2 | import React, { FC, useEffect } from "react"; 3 | import styles from "../socialButtons.module.css"; 4 | 5 | interface InstagramButtonProps { 6 | children?: string; 7 | onClick?: () => void; 8 | callbackURL?: string; 9 | } 10 | 11 | const InstagramButton: FC = ({ 12 | children = "Login With Instagram", 13 | callbackURL, 14 | onClick = () => 15 | signIn({ 16 | provider: "instagram", 17 | callbackURL: callbackURL, 18 | }), 19 | }) => { 20 | return ( 21 | 30 | ); 31 | }; 32 | 33 | export default InstagramButton; 34 | -------------------------------------------------------------------------------- /packages/astro-auth-ui/src/components/MetamaskButton/index.tsx: -------------------------------------------------------------------------------- 1 | import { signIn } from "@astro-auth/client"; 2 | import React, { FC, useEffect } from "react"; 3 | import styles from "../socialButtons.module.css"; 4 | 5 | interface MetamaskButtonProps { 6 | children?: string; 7 | onClick?: () => void; 8 | callbackURL?: string; 9 | } 10 | 11 | const MetamaskButton: FC = ({ 12 | children = "Login With Metamask", 13 | callbackURL, 14 | onClick = () => 15 | signIn({ 16 | provider: "metamask", 17 | callbackURL: callbackURL, 18 | }), 19 | }) => { 20 | return ( 21 | 30 | ); 31 | }; 32 | 33 | export default MetamaskButton; 34 | -------------------------------------------------------------------------------- /packages/astro-auth-ui/src/components/SpotifyButton/index.tsx: -------------------------------------------------------------------------------- 1 | import { signIn } from "@astro-auth/client"; 2 | import React, { FC, useEffect } from "react"; 3 | import styles from "../socialButtons.module.css"; 4 | 5 | interface SpotifyButtonProps { 6 | children?: string; 7 | onClick?: () => void; 8 | callbackURL?: string; 9 | } 10 | 11 | const SpotifyButton: FC = ({ 12 | children = "Login With Spotify", 13 | callbackURL, 14 | onClick = () => 15 | signIn({ 16 | provider: "discord", 17 | callbackURL: callbackURL, 18 | }), 19 | }) => { 20 | return ( 21 | 30 | ); 31 | }; 32 | 33 | export default SpotifyButton; 34 | -------------------------------------------------------------------------------- /packages/astro-auth-ui/src/components/Title/index.module.css: -------------------------------------------------------------------------------- 1 | .text { 2 | font-size: 4rem; 3 | background: linear-gradient(180deg, #1d5bfc 0%, #c238bd 115%); 4 | -webkit-background-clip: text; 5 | background-clip: text; 6 | -webkit-text-fill-color: transparent; 7 | font-family: "RTAliasMedium", monospace; 8 | } 9 | -------------------------------------------------------------------------------- /packages/astro-auth-ui/src/components/Title/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC } from "react"; 2 | import styles from "./index.module.css"; 3 | 4 | interface TitleProps { 5 | children: string; 6 | as?: "h1" | "h2" | "h3" | "h4" | "h5" | "h6" | "p" | "span"; 7 | } 8 | 9 | const Title: FC = ({ children, as: As = "h2" }) => { 10 | return {children}; 11 | }; 12 | 13 | export default Title; 14 | -------------------------------------------------------------------------------- /packages/astro-auth-ui/src/components/TwitterButton/index.tsx: -------------------------------------------------------------------------------- 1 | import { signIn } from "@astro-auth/client"; 2 | import React, { FC } from "react"; 3 | import styles from "../socialButtons.module.css"; 4 | 5 | interface TwitterButtonProps { 6 | children?: string; 7 | onClick?: () => void; 8 | callbackURL?: string; 9 | } 10 | 11 | const TwitterButton: FC = ({ 12 | children = "Login With Twitter", 13 | callbackURL, 14 | onClick = () => 15 | signIn({ 16 | provider: "twitter", 17 | callbackURL: callbackURL, 18 | }), 19 | }) => { 20 | return ( 21 | 30 | ); 31 | }; 32 | 33 | export default TwitterButton; 34 | -------------------------------------------------------------------------------- /packages/astro-auth-ui/src/components/ZoomButton/index.tsx: -------------------------------------------------------------------------------- 1 | import { signIn } from "@astro-auth/client"; 2 | import React, { FC, useEffect } from "react"; 3 | import styles from "../socialButtons.module.css"; 4 | 5 | interface ZoomButtonProps { 6 | children?: string; 7 | onClick?: () => void; 8 | callbackURL?: string; 9 | } 10 | 11 | const ZoomButton: FC = ({ 12 | children = "Login With Zoom", 13 | callbackURL, 14 | onClick = () => 15 | signIn({ 16 | provider: "discord", 17 | callbackURL: callbackURL, 18 | }), 19 | }) => { 20 | return ( 21 | 30 | ); 31 | }; 32 | 33 | export default ZoomButton; 34 | -------------------------------------------------------------------------------- /packages/astro-auth-ui/src/components/socialButtons.module.css: -------------------------------------------------------------------------------- 1 | .button { 2 | border: none; 3 | background: none; 4 | position: relative; 5 | --border-radius: 8; 6 | --pixel-size: 4; 7 | border-radius: calc(var(--border-radius) * 1px); 8 | padding: 0.67rem 2.5rem; 9 | cursor: pointer; 10 | color: #fff; 11 | font-size: 1.3rem; 12 | display: flex; 13 | justify-content: center; 14 | align-items: center; 15 | padding-top: 1rem; 16 | transition: 0.2s transform, 0.2s background; 17 | font-family: "RTAliasMedium", monospace; 18 | 19 | --link-color-stop-a: #1d5bfc; 20 | --link-color-stop-b: #c238bd; 21 | } 22 | 23 | 24 | .button img { 25 | margin-right: 0.5rem; 26 | margin-bottom: 0.25rem; 27 | } 28 | 29 | .button:hover { 30 | transform: translateY(-3px); 31 | 32 | --link-color-stop-a: #6d39ff; 33 | --link-color-stop-b: #af43ff; 34 | } 35 | 36 | .button:active { 37 | transform: translateY(0); 38 | 39 | --link-color-stop-a: #1d5bfc; 40 | --link-color-stop-b: #c238bd; 41 | } 42 | 43 | .button::before { 44 | content: ""; 45 | position: absolute; 46 | top: calc(var(--pixel-size) * 1px); 47 | right: 0; 48 | bottom: calc(var(--pixel-size) * 1px); 49 | left: 0; 50 | background: linear-gradient( 51 | 180deg, 52 | var(--link-color-stop-a), 53 | var(--link-color-stop-b) 54 | ); 55 | z-index: -1; 56 | } 57 | 58 | .button::after { 59 | content: ""; 60 | position: absolute; 61 | top: 0; 62 | right: calc(var(--pixel-size) * 1px); 63 | bottom: 0; 64 | left: calc(var(--pixel-size) * 1px); 65 | background: linear-gradient( 66 | 180deg, 67 | var(--link-color-stop-a), 68 | var(--link-color-stop-b) 69 | ); 70 | z-index: -1; 71 | } 72 | -------------------------------------------------------------------------------- /packages/astro-auth-ui/src/index.css: -------------------------------------------------------------------------------- 1 | /* Hosted By Myself */ 2 | @import url("https://rt-alias-medium.vercel.app/stylesheet.css"); 3 | 4 | 5 | -------------------------------------------------------------------------------- /packages/astro-auth-ui/src/index.ts: -------------------------------------------------------------------------------- 1 | import "./index.css"; 2 | 3 | import Button from "./components/Button"; 4 | import Title from "./components/Title"; 5 | 6 | import GoogleButton from "./components/GoogleButton"; 7 | import DiscordButton from "./components/DiscordButton"; 8 | import TwitterButton from "./components/TwitterButton"; 9 | import FacebookButton from "./components/FacebookButton"; 10 | import GithubButton from "./components/GithubButton"; 11 | import InstagramButton from "./components/InstagramButton"; 12 | import SpotifyButton from "./components/SpotifyButton"; 13 | import ZoomButton from "./components/ZoomButton"; 14 | 15 | import MetamaskButton from "./components/MetamaskButton"; 16 | 17 | export { 18 | Button, 19 | Title, 20 | GoogleButton, 21 | DiscordButton, 22 | TwitterButton, 23 | FacebookButton, 24 | GithubButton, 25 | InstagramButton, 26 | SpotifyButton, 27 | ZoomButton, 28 | MetamaskButton, 29 | }; 30 | -------------------------------------------------------------------------------- /packages/astro-auth-ui/src/modules.d.ts: -------------------------------------------------------------------------------- 1 | declare module "*.module.css" { 2 | const classes: { [key: string]: string }; 3 | export default classes; 4 | } 5 | -------------------------------------------------------------------------------- /packages/astro-auth-ui/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6", 4 | "esModuleInterop": true, 5 | "forceConsistentCasingInFileNames": true, 6 | "strict": true, 7 | "skipLibCheck": true, 8 | "jsx": "react", 9 | "module": "ESNext", 10 | "declaration": true, 11 | "declarationDir": "types", 12 | "sourceMap": true, 13 | "outDir": "dist", 14 | "moduleResolution": "node", 15 | "allowSyntheticDefaultImports": true, 16 | "emitDeclarationOnly": true 17 | } 18 | } 19 | --------------------------------------------------------------------------------