├── .eslintrc.json
├── .gitignore
├── .husky
└── pre-commit
├── .prettierrc
├── LICENSE
├── README.md
├── env.d.ts
├── example
├── App.vue
├── assets
│ └── main.css
├── main.ts
├── router.ts
└── views
│ ├── Dashboard.vue
│ ├── Login.vue
│ └── ResetPassword.vue
├── favicon.ico
├── index.html
├── package-lock.json
├── package.json
├── src
├── components
│ ├── AuthorizerBasicAuthLogin.vue
│ ├── AuthorizerForgotPassword.vue
│ ├── AuthorizerMagicLinkLogin.vue
│ ├── AuthorizerProvider.vue
│ ├── AuthorizerResetPassword.vue
│ ├── AuthorizerRoot.vue
│ ├── AuthorizerSignup.vue
│ ├── AuthorizerSocialLogin.vue
│ ├── AuthorizerVerifyOtp.vue
│ ├── Message.vue
│ └── PasswordStrengthIndicator.vue
├── constants
│ └── index.ts
├── icons
│ ├── Apple.vue
│ ├── Close.vue
│ ├── Discord.vue
│ ├── Facebook.vue
│ ├── Github.vue
│ ├── Google.vue
│ ├── Linkedin.vue
│ ├── Microsoft.vue
│ ├── Roblox.vue
│ └── Twitter.vue
├── index.ts
├── shims-vue.d.ts
├── state
│ ├── globalConfig.ts
│ └── globalContext.ts
├── styledComponents
│ ├── StyledButton.vue
│ ├── StyledFlex.vue
│ ├── StyledFooter.vue
│ ├── StyledLink.vue
│ ├── StyledMessageWrapper.vue
│ ├── StyledPasswordStrength.vue
│ ├── StyledPasswordStrengthWrapper.vue
│ ├── StyledSeparator.vue
│ ├── StyledWrapper.vue
│ └── index.ts
├── styles
│ └── default.css
├── types
│ └── index.ts
└── utils
│ ├── common.ts
│ ├── url.ts
│ └── window.ts
├── tsconfig.app.json
├── tsconfig.build-types.json
├── tsconfig.config.json
├── tsconfig.json
└── vite.config.ts
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "parser": "vue-eslint-parser", // Use the vue-eslint-parser to parse Vue.js files
3 | "parserOptions": {
4 | "parser": "@typescript-eslint/parser", // Use the @typescript-eslint/parser to parse TypeScript files
5 | "ecmaVersion": 2020,
6 | "sourceType": "module"
7 | },
8 | "extends": [
9 | "plugin:vue/recommended", // Use the recommended rules for Vue.js from the vue plugin
10 | "plugin:prettier-vue/recommended", // Use the recommended rules for Prettier with Vue.js from the prettier-vue plugin
11 | "plugin:@typescript-eslint/recommended" // Use the recommended rules for TypeScript from the @typescript-eslint plugin
12 | ],
13 | "settings": {
14 | "prettier-vue": {
15 | "SFCBlocks": {
16 | // Specify which blocks in Vue SFCs should be processed by Prettier
17 | "template": true,
18 | "script": true,
19 | "style": true,
20 | "customBlocks": {
21 | "docs": { "lang": "markdown" },
22 | "config": { "lang": "json" },
23 | "module": { "lang": "js" },
24 | "comments": false
25 | }
26 | },
27 | "usePrettierrc": true, // Use the Prettier configuration file in the project root
28 | "fileInfoOptions": {
29 | "ignorePath": ".testignore", // Ignore files listed in the .testignore file
30 | "withNodeModules": false // Ignore files in the node_modules directory
31 | }
32 | }
33 | },
34 | "rules": {
35 | "prettier-vue/prettier": [
36 | "error",
37 | {
38 | // Specify Prettier options for Vue.js files
39 | "useTabs": true,
40 | "printWidth": 100,
41 | "singleQuote": true,
42 | "trailingComma": "none"
43 | }
44 | ],
45 | "@typescript-eslint/no-unused-vars": [
46 | "error",
47 | { "argsIgnorePattern": "^_", "varsIgnorePattern": "^_" } // Ignore unused variables that start with an underscore in TypeScript files
48 | ],
49 | "vue/multi-word-component-names": "off" // Disable the multi-word-component-names rule from the vue plugin
50 | },
51 | "overrides": [
52 | {
53 | "files": ["*.ts", "*.tsx"],
54 | "rules": {
55 | "no-undef": "off" // Disable the no-undef rule for TypeScript files
56 | }
57 | }
58 | ]
59 | }
60 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.log
2 | .DS_Store
3 | node_modules
4 | .cache
5 | dist
6 | .parcel-cache
7 | .yalc
--------------------------------------------------------------------------------
/.husky/pre-commit:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 | . "$(dirname -- "$0")/_/husky.sh"
3 |
4 | git stash --keep-index -u
5 | npm run format
6 | git add .
7 | npm run lint
8 | git stash apply
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "useTabs": true,
3 | "singleQuote": true,
4 | "trailingComma": "none",
5 | "printWidth": 100
6 | }
7 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 authorizer
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # authorizer-vue
2 |
3 | Authorizer Vue SDK allows you to implement authentication in your [Vue](https://vuejs.org/) application quickly. It also allows you to access the user profile.
4 |
5 | Here is a quick guide on getting started with `@authorizerdev/authorizer-vue` package.
6 |
7 |
8 |
9 | ## Code Sandbox Demo: https://codesandbox.io/s/authorizer-vue-example-700l1h
10 |
11 | ## Step 1 - Create Instance
12 |
13 | Get Authorizer URL by instantiating [Authorizer instance](/deployment) and configuring it with necessary [environment variables](/core/env).
14 |
15 | ## Step 2 - Install package
16 |
17 | Install `@authorizerdev/authorizer-vue` library
18 |
19 | ```sh
20 | npm i --save @authorizerdev/authorizer-vue
21 | OR
22 | yarn add @authorizerdev/authorizer-vue
23 | ```
24 |
25 | ## Step 3 - Configure Provider and use Authorizer Components
26 |
27 | Authorizer comes with a [Provider](https://vuejs.org/api/composition-api-dependency-injection.html#provide) component that exposes a composable function to return a [reactive](https://vuejs.org/api/reactivity-core.html#reactive) context to it's children by using the `useAuthorizer` injection key, internally [toRefs](https://vuejs.org/api/reactivity-utilities.html#torefs) are used when returning the reactive state so that the consuming component(s) can destructure/spread the returned object without losing reactivity and each property could be watched to perform actions accordingly.
28 |
29 | ```vue
30 |
31 |
43 |
44 |
45 |
64 | ```
65 |
66 | ```vue
67 |
68 |
69 |
Welcome to Authorizer
70 |
71 |
72 |
73 |
74 |
75 |
116 | ```
117 |
118 | ## Commands
119 |
120 | ### Local Development
121 |
122 | ### The recommended workflow is to run authorizer in one terminal:
123 |
124 | ```bash
125 | npm run dev # or yarn dev
126 | ```
127 |
128 | This starts a local dev-server with a sandbox environment.
129 |
130 | ```bash
131 | npm run build # or yarn build
132 | ```
133 |
134 | This uses Vite to build the project files to `/dist` and call `build:types` script.
135 |
136 | ```bash
137 | npm run build:types # or yarn build:types
138 | ```
139 |
140 | This generates TypeScript declaration files for our .vue files (using `vue-tsc`).
141 |
142 | ```bash
143 | npm run typecheck # or yarn typecheck
144 | ```
145 |
146 | This runs a typecheck against our Vue components to make sure there are no type errors (using `vue-tsc`).
147 |
148 | ## Configuration
149 |
150 | ### Typescript:
151 |
152 | - Root tsconfig: `tsconfig.json` contains a reference to all the others tsconfig files.
153 | - Components tsconfig: `tsconfig.app.json` will take care of compiling our Vue components inside `src/`.
154 | - Build-types tsconfig: `tsconfig.build-types.json` will take care of generating the proper types declaration files of our Vue components inside `src/`.
155 | - Tools tsconfig: `tsconfig.config.json` will take care of all our tooling configuration files (we only have vite at the moment).
156 |
157 | ### Vite:
158 |
159 | - Vite requires a configuration to compile and bundle `.vue` to `.js` files that can be consumed through an npm module. It uses [rollup.js](https://rollupjs.org/) under the hood, check out the comments in `vite.config.ts` file in the project root to learn more about the configuarition details.
160 |
161 | ### Eslint:
162 |
163 | - All required linting configurations are specified in the `.elsintrc.json` file in the project root, check the comments in each section to learn more about the configuarition details.
164 |
165 | ### Prettier:
166 |
167 | - We have the `"usePrettierrc"` option set to true in the `eslint` configuration file which tells the `prettier-vue` plugin to use the Prettier configuration file `.prettierrc` in the project root directory and override any default settings.
168 |
169 | ### Husky:
170 |
171 | - A pre-commit hook is set in `.husky/pre-commit` which formats the code and checks for any linting errors.
172 |
--------------------------------------------------------------------------------
/env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/example/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
23 |
24 |
25 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/example/assets/main.css:
--------------------------------------------------------------------------------
1 | body {
2 | font-family: -apple-system, system-ui, sans-serif;
3 | color: #374151;
4 | font-size: 14px;
5 | }
6 |
7 | *,
8 | *:before,
9 | *:after {
10 | box-sizing: inherit;
11 | }
12 |
13 | #app {
14 | display: flex;
15 | height: 100vh;
16 | flex-direction: column;
17 | justify-content: center;
18 | align-items: center;
19 | }
20 |
--------------------------------------------------------------------------------
/example/main.ts:
--------------------------------------------------------------------------------
1 | import { createApp } from 'vue';
2 |
3 | import App from './App.vue';
4 | import router from './router';
5 | import '../src/styles/default.css';
6 | import './assets/main.css';
7 |
8 | createApp(App).use(router).mount('#app');
9 |
--------------------------------------------------------------------------------
/example/router.ts:
--------------------------------------------------------------------------------
1 | import { createRouter, createWebHistory } from 'vue-router';
2 | import Login from './views/Login.vue';
3 | import ResetPassword from './views/ResetPassword.vue';
4 | import Dashboard from './views/Dashboard.vue';
5 |
6 | export default createRouter({
7 | history: createWebHistory(),
8 | routes: [
9 | {
10 | path: '/',
11 | component: Login
12 | },
13 | {
14 | path: '/reset-password',
15 | component: ResetPassword
16 | },
17 | {
18 | path: '/dashboard',
19 | component: Dashboard
20 | }
21 | ]
22 | });
23 |
--------------------------------------------------------------------------------
/example/views/Dashboard.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Hey 👋,
4 |
Thank you for joining Authorizer demo app.
5 |
6 | Your email address is
7 |
13 | {{ user?.email }}
14 |
15 |
16 |
17 |
18 |
Processing....
19 |
27 | Logout
28 |
29 |
30 |
31 |
32 |
61 |
62 |
63 |
--------------------------------------------------------------------------------
/example/views/Login.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Welcome to Authorizer
4 |
5 |
6 |
7 |
8 |
9 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/example/views/ResetPassword.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Reset Password
4 |
5 |
6 |
7 |
8 |
9 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/authorizerdev/authorizer-vue/5cd64fc498a1b8c8bf605987160e179104b0ebd1/favicon.ico
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | Authorizer Demo App
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@authorizerdev/authorizer-vue",
3 | "version": "1.0.0",
4 | "description": "authorizer vue sdk",
5 | "files": [
6 | "dist"
7 | ],
8 | "main": "dist/@authorizerdev/authorizer-vue.umd.js",
9 | "module": "dist/@authorizerdev/authorizer-vue.es.js",
10 | "types": "dist/types/index.d.ts",
11 | "scripts": {
12 | "dev": "vite",
13 | "build": "vite build && npm run build:types",
14 | "build:types": "vue-tsc --project tsconfig.build-types.json --declaration --emitDeclarationOnly --outDir dist/types ",
15 | "typecheck": "vue-tsc --project tsconfig.build-types.json --noEmit",
16 | "lint": "prettier --plugin-search-dir . --check . --ignore-path .gitignore && eslint . --ignore-path .gitignore",
17 | "format": "prettier --plugin-search-dir . --write . --ignore-path .gitignore",
18 | "prepare": "husky install"
19 | },
20 | "keywords": [],
21 | "author": "Lakhan Samani",
22 | "license": "MIT",
23 | "bugs": {
24 | "url": "https://github.com/authorizerdev/authorizer-vue/issues"
25 | },
26 | "homepage": "https://github.com/authorizerdev/authorizer-vue#readme",
27 | "devDependencies": {
28 | "@babel/types": "^7.21.4",
29 | "@tsconfig/node18": "^2.0.0",
30 | "@types/node": "^18.15.13",
31 | "@typescript-eslint/eslint-plugin": "^5.59.1",
32 | "@typescript-eslint/parser": "^5.59.1",
33 | "@vitejs/plugin-vue": "^4.1.0",
34 | "@vue/tsconfig": "^0.2.0",
35 | "eslint": "^8.39.0",
36 | "eslint-config-prettier": "^8.8.0",
37 | "eslint-plugin-prettier-vue": "^4.2.0",
38 | "eslint-plugin-vue": "^9.11.0",
39 | "husky": "^8.0.3",
40 | "prettier": "^2.8.8",
41 | "sass": "^1.62.0",
42 | "typescript": "^5.0.4",
43 | "vite": "^4.3.1",
44 | "vue": "^3.2.47",
45 | "vue-eslint-parser": "^9.1.1",
46 | "vue-router": "^4.1.6",
47 | "vue-tsc": "^1.4.2"
48 | },
49 | "dependencies": {
50 | "@authorizerdev/authorizer-js": "^1.2.3"
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/components/AuthorizerBasicAuthLogin.vue:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
12 |
13 |
14 |
15 |
54 |
55 |
56 | setView(Views.ForgotPassword)"
59 | >
60 | Forgot Password?
61 |
62 |
63 | Don't have an account?
64 | setView(Views.Signup)">Sign Up
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
214 |
215 |
218 |
--------------------------------------------------------------------------------
/src/components/AuthorizerForgotPassword.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | Please enter your email address.
11 |
12 | We will send you an email to reset your password.
13 |
14 |
15 |
36 |
37 |
38 |
39 | Remember your password?
40 | setView(Views.Login)">Log In
41 |
42 |
43 |
44 |
45 |
46 |
47 |
138 |
139 |
142 |
--------------------------------------------------------------------------------
/src/components/AuthorizerMagicLinkLogin.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
34 |
35 |
36 |
135 |
136 |
139 |
--------------------------------------------------------------------------------
/src/components/AuthorizerProvider.vue:
--------------------------------------------------------------------------------
1 |
215 |
--------------------------------------------------------------------------------
/src/components/AuthorizerResetPassword.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
62 |
63 |
64 |
65 |
175 |
176 |
179 |
--------------------------------------------------------------------------------
/src/components/AuthorizerRoot.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
15 |
27 |
33 |
39 |
40 |
41 |
42 |
123 |
--------------------------------------------------------------------------------
/src/components/AuthorizerSignup.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
80 |
81 |
82 |
83 | Already have an account?
84 | setView(Views.Login)">Log In
85 |
86 |
87 |
88 |
89 |
90 |
91 |
262 |
265 |
--------------------------------------------------------------------------------
/src/components/AuthorizerSocialLogin.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 | window
9 | ? (window.location.href = `${config.authorizerURL.value}/oauth_login/apple?${queryParams}`)
10 | : null
11 | "
12 | >
13 |
14 | Sign in with Apple
15 |
16 |
17 |
18 |
19 |
23 | window
24 | ? (window.location.href = `${config.authorizerURL.value}/oauth_login/google?${queryParams}`)
25 | : null
26 | "
27 | >
28 |
29 | Sign in with Google
30 |
31 |
32 |
33 |
34 |
38 | window
39 | ? (window.location.href = `${config.authorizerURL.value}/oauth_login/github?${queryParams}`)
40 | : null
41 | "
42 | >
43 |
44 | Sign in with Github
45 |
46 |
47 |
48 |
49 |
53 | window
54 | ? (window.location.href = `${config.authorizerURL.value}/oauth_login/facebook?${queryParams}`)
55 | : null
56 | "
57 | >
58 |
59 | Sign in with Facebook
60 |
61 |
62 |
63 |
64 |
68 | window
69 | ? (window.location.href = `${config.authorizerURL.value}/oauth_login/linkedin?${queryParams}`)
70 | : null
71 | "
72 | >
73 |
74 | Sign in with Linkedin
75 |
76 |
77 |
78 |
79 |
83 | window
84 | ? (window.location.href = `${config.authorizerURL.value}/oauth_login/twitter?${queryParams}`)
85 | : null
86 | "
87 | >
88 |
89 | Sign in with Twitter
90 |
91 |
92 |
93 |
94 |
98 | window
99 | ? (window.location.href = `${config.authorizerURL.value}/oauth_login/microsoft?${queryParams}`)
100 | : null
101 | "
102 | >
103 |
104 | Sign in with Microsoft
105 |
106 |
107 |
108 |
109 |
113 | window
114 | ? (window.location.href = `${config.authorizerURL.value}/oauth_login/discord?${queryParams}`)
115 | : null
116 | "
117 | >
118 |
119 |
120 |
121 |
122 |
123 |
127 | window
128 | ? (window.location.href = `${config.authorizerURL.value}/oauth_login/roblox?${queryParams}`)
129 | : null
130 | "
131 | >
132 |
133 |
134 |
135 |
136 |
142 | OR
143 |
144 |
145 |
146 |
147 |
224 |
--------------------------------------------------------------------------------
/src/components/AuthorizerVerifyOtp.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | Please enter the OTP you received on your email address.
11 |
12 |
13 |
34 |
35 |
36 | Sending ...
37 |
38 | Resend OTP
39 |
40 |
41 | Don't have an account?
42 | setView(Views.Signup)">Sign Up
43 |
44 |
45 |
46 |
47 |
48 |
49 |
186 |
189 |
--------------------------------------------------------------------------------
/src/components/Message.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | {{ capitalizeFirstLetter(text) }}
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
46 |
--------------------------------------------------------------------------------
/src/components/PasswordStrengthIndicator.vue:
--------------------------------------------------------------------------------
1 |
2 |
78 |
79 |
80 |
147 |
148 |
151 |
--------------------------------------------------------------------------------
/src/constants/index.ts:
--------------------------------------------------------------------------------
1 | export enum AuthorizerProviderActionType {
2 | SET_USER = 'SET_USER',
3 | SET_TOKEN = 'SET_TOKEN',
4 | SET_LOADING = 'SET_LOADING',
5 | SET_AUTH_DATA = 'SET_AUTH_DATA',
6 | SET_CONFIG = 'SET_CONFIG'
7 | }
8 |
9 | export enum Views {
10 | Login = 'Login',
11 | Signup = 'Signup',
12 | ForgotPassword = 'ForgotPassword'
13 | }
14 |
15 | export const ButtonAppearance: Record = {
16 | Primary: 'Primary',
17 | Default: 'Default'
18 | };
19 |
20 | export const MessageType: Record = {
21 | Error: 'Error',
22 | Success: 'Success'
23 | };
24 |
25 | // TODO use based on theme primary color
26 | export const passwordStrengthIndicatorOpacity: Record = {
27 | default: 0.15,
28 | weak: 0.4,
29 | good: 0.6,
30 | strong: 0.8,
31 | veryStrong: 1
32 | };
33 |
--------------------------------------------------------------------------------
/src/icons/Apple.vue:
--------------------------------------------------------------------------------
1 |
2 |
16 |
17 |
18 |
23 |
--------------------------------------------------------------------------------
/src/icons/Close.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
9 |
10 |
11 |
12 |
17 |
--------------------------------------------------------------------------------
/src/icons/Discord.vue:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/src/icons/Facebook.vue:
--------------------------------------------------------------------------------
1 |
2 |
22 |
23 |
24 |
29 |
--------------------------------------------------------------------------------
/src/icons/Github.vue:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
19 |
24 |
--------------------------------------------------------------------------------
/src/icons/Google.vue:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
12 |
16 |
20 |
24 |
28 |
29 |
30 |
31 |
32 |
33 |
38 |
--------------------------------------------------------------------------------
/src/icons/Linkedin.vue:
--------------------------------------------------------------------------------
1 |
2 |
21 |
22 |
23 |
28 |
--------------------------------------------------------------------------------
/src/icons/Microsoft.vue:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
24 |
--------------------------------------------------------------------------------
/src/icons/Roblox.vue:
--------------------------------------------------------------------------------
1 |
2 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/src/icons/Twitter.vue:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
19 |
24 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | import AuthorizerProvider from './components/AuthorizerProvider.vue';
2 | import AuthorizerSignup from './components/AuthorizerSignup.vue';
3 | import AuthorizerBasicAuthLogin from './components/AuthorizerBasicAuthLogin.vue';
4 | import AuthorizerMagicLinkLogin from './components/AuthorizerMagicLinkLogin.vue';
5 | import AuthorizerForgotPassword from './components/AuthorizerForgotPassword.vue';
6 | import AuthorizerSocialLogin from './components/AuthorizerSocialLogin.vue';
7 | import AuthorizerResetPassword from './components/AuthorizerResetPassword.vue';
8 | import AuthorizerVerifyOtp from './components/AuthorizerVerifyOtp.vue';
9 | import AuthorizerRoot from './components/AuthorizerRoot.vue';
10 |
11 | export {
12 | AuthorizerProvider,
13 | AuthorizerSignup,
14 | AuthorizerBasicAuthLogin,
15 | AuthorizerMagicLinkLogin,
16 | AuthorizerForgotPassword,
17 | AuthorizerSocialLogin,
18 | AuthorizerResetPassword,
19 | AuthorizerVerifyOtp,
20 | AuthorizerRoot
21 | };
22 |
--------------------------------------------------------------------------------
/src/shims-vue.d.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | // bridge between TypeScript and Vue, providing type definitions and declarations
3 | // that help TypeScript understand Vue's components and syntax
4 | declare module '*.vue' {
5 | import { DefineComponent } from 'vue';
6 | const component: DefineComponent;
7 | export default component;
8 | }
9 |
--------------------------------------------------------------------------------
/src/state/globalConfig.ts:
--------------------------------------------------------------------------------
1 | import type { AuthorizerConfig } from '../types';
2 | import { reactive } from 'vue';
3 | export default reactive({
4 | authorizerURL: '',
5 | redirectURL: '',
6 | client_id: '',
7 | is_google_login_enabled: false,
8 | is_github_login_enabled: false,
9 | is_facebook_login_enabled: false,
10 | is_linkedin_login_enabled: false,
11 | is_apple_login_enabled: false,
12 | is_discord_login_enabled: false,
13 | is_roblox_login_enabled: false,
14 | is_email_verification_enabled: false,
15 | is_basic_authentication_enabled: false,
16 | is_magic_link_login_enabled: false,
17 | is_sign_up_enabled: false,
18 | is_strong_password_enabled: true,
19 | is_twitter_login_enabled: false,
20 | is_microsoft_login_enabled: false
21 | }) as AuthorizerConfig;
22 |
--------------------------------------------------------------------------------
/src/state/globalContext.ts:
--------------------------------------------------------------------------------
1 | import { reactive } from 'vue';
2 | import { Authorizer } from '@authorizerdev/authorizer-js';
3 | import type { AuthorizerContextPropsType } from '../types';
4 | import { hasWindow } from '../utils/window';
5 | export default reactive({
6 | user: null,
7 | token: null,
8 | loading: false,
9 | setLoading: () => undefined,
10 | setToken: () => undefined,
11 | setUser: () => undefined,
12 | setAuthData: () => undefined,
13 | authorizerRef: new Authorizer({
14 | authorizerURL: `http://localhost:8080`,
15 | redirectURL: hasWindow() ? window.location.origin : '/',
16 | clientID: ''
17 | }),
18 | logout: async () => undefined
19 | }) as AuthorizerContextPropsType;
20 |
--------------------------------------------------------------------------------
/src/styledComponents/StyledButton.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
40 |
41 |
44 |
--------------------------------------------------------------------------------
/src/styledComponents/StyledFlex.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
49 |
--------------------------------------------------------------------------------
/src/styledComponents/StyledFooter.vue:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
12 |
13 |
16 |
--------------------------------------------------------------------------------
/src/styledComponents/StyledLink.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
28 |
29 |
32 |
--------------------------------------------------------------------------------
/src/styledComponents/StyledMessageWrapper.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
32 |
33 |
36 |
--------------------------------------------------------------------------------
/src/styledComponents/StyledPasswordStrength.vue:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
9 |
10 |
29 |
30 |
33 |
--------------------------------------------------------------------------------
/src/styledComponents/StyledPasswordStrengthWrapper.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
16 |
--------------------------------------------------------------------------------
/src/styledComponents/StyledSeparator.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
16 |
--------------------------------------------------------------------------------
/src/styledComponents/StyledWrapper.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
16 |
--------------------------------------------------------------------------------
/src/styledComponents/index.ts:
--------------------------------------------------------------------------------
1 | import StyledWrapper from './StyledWrapper.vue';
2 | import StyledButton from './StyledButton.vue';
3 | import StyledLink from './StyledLink.vue';
4 | import StyledSeparator from './StyledSeparator.vue';
5 | import StyledFooter from './StyledFooter.vue';
6 | import StyledMessageWrapper from './StyledMessageWrapper.vue';
7 | import StyledFlex from './StyledFlex.vue';
8 | import StyledPasswordStrength from './StyledPasswordStrength.vue';
9 | import StyledPasswordStrengthWrapper from './StyledPasswordStrengthWrapper.vue';
10 |
11 | export {
12 | StyledWrapper,
13 | StyledButton,
14 | StyledLink,
15 | StyledSeparator,
16 | StyledFooter,
17 | StyledMessageWrapper,
18 | StyledFlex,
19 | StyledPasswordStrength,
20 | StyledPasswordStrengthWrapper
21 | };
22 |
--------------------------------------------------------------------------------
/src/styles/default.css:
--------------------------------------------------------------------------------
1 | * {
2 | --authorizer-primary-color: #3b82f6;
3 | --authorizer-primary-disabled-color: #60a5fa;
4 | --authorizer-gray-color: #d1d5db;
5 | --authorizer-white-color: #ffffff;
6 | --authorizer-danger-color: #dc2626;
7 | --authorizer-success-color: #10b981;
8 | --authorizer-text-color: #374151;
9 | --authorizer-fonts-font-stack: -apple-system, system-ui, sans-serif;
10 | --authorizer-fonts-large-text: 18px;
11 | --authorizer-fonts-medium-text: 14px;
12 | --authorizer-fonts-small-text: 12px;
13 | --authorizer-fonts-tiny-text: 10px;
14 | --authorizer-radius-card: 5px;
15 | --authorizer-radius-button: 5px;
16 | --authorizer-radius-input: 5px;
17 | }
18 | .styled-form-group {
19 | width: 100%;
20 | border: 0px;
21 | background-color: var(--authorizer-white-color);
22 | padding: 0 0 15px;
23 | }
24 | .form-input-label {
25 | padding: 2.5px;
26 | }
27 | .form-input-label > span {
28 | color: var(--authorizer-danger-color);
29 | }
30 | .form-input-field {
31 | width: 100%;
32 | margin-top: 5px;
33 | padding: 10px;
34 | display: flex;
35 | flex-direction: column;
36 | align-items: center;
37 | border-radius: var(--authorizer-radius-input);
38 | border: 1px;
39 | border-style: solid;
40 | border-color: var(--authorizer-text-color);
41 | }
42 | .input-error-content {
43 | border-color: var(--authorizer-danger-color) !important;
44 | }
45 | .input-error-content:hover {
46 | outline-color: var(--authorizer-danger-color);
47 | }
48 | .input-error-content:focus {
49 | outline-color: var(--authorizer-danger-color);
50 | }
51 | .form-input-error {
52 | font-size: 12px;
53 | font-weight: 400;
54 | color: red;
55 | border-color: var(--authorizer-danger-color);
56 | }
57 | .styled-check-box-label {
58 | margin-left: 5px;
59 | }
60 | .styled-button {
61 | padding: 15px 10px !important;
62 | display: flex;
63 | justify-content: center;
64 | align-items: center;
65 | min-width: 375px;
66 | max-height: 64px;
67 | border-radius: var(--authorizer-radius-button);
68 | border-color: var(--authorizer-text-color) !important;
69 | border-style: solid !important;
70 | cursor: pointer;
71 | position: relative;
72 | }
73 | .styled-button:disabled {
74 | cursor: not-allowed;
75 | background-color: var(--authorizer-primary-disabled-color);
76 | }
77 | .styled-footer {
78 | display: flex;
79 | flex-direction: column;
80 | justify-content: center;
81 | align-items: center;
82 | margin-top: 15px;
83 | }
84 | .styled-link {
85 | color: var(--authorizer-primary-color);
86 | cursor: pointer;
87 | }
88 | .styled-message-wrapper {
89 | padding: 10px;
90 | color: white;
91 | border-radius: var(--authorizer-radius-card);
92 | margin: 10px 0px;
93 | font-size: var(--authorizer-fonts-small-text);
94 | }
95 | .styled-password-strength {
96 | width: 100%;
97 | height: 10px;
98 | flex: 0.75;
99 | border-radius: 5px;
100 | margin-right: 5px;
101 | background-color: var(--authorizer-primary-color);
102 | }
103 | .styled-password-strength-wrapper {
104 | margin: 2% 0 0;
105 | }
106 | .styled-separator {
107 | display: flex;
108 | align-items: center;
109 | text-align: center;
110 | margin: 10px 0px;
111 | }
112 | .styled-separator::before {
113 | content: '';
114 | flex: 1;
115 | border-bottom: 1px solid var(--authorizer-gray-color);
116 | }
117 | .styled-separator::after {
118 | content: '';
119 | flex: 1;
120 | border-bottom: 1px solid var(--authorizer-gray-color);
121 | }
122 | .styled-separator:not(:empty)::before {
123 | margin-right: 0.25em;
124 | }
125 | .styled-separator:not(:empty)::after {
126 | margin-left: 0.25em;
127 | }
128 | .styled-wrapper {
129 | font-family: var(--authorizer-fonts-font-stack);
130 | color: var(--authorizer-text-color);
131 | font-size: var(--authorizer-fonts-medium-text);
132 | box-sizing: border-box;
133 | width: 100%;
134 | }
135 | .styled-wrapper *,
136 | *:before,
137 | *:after {
138 | box-sizing: inherit;
139 | }
140 |
--------------------------------------------------------------------------------
/src/types/index.ts:
--------------------------------------------------------------------------------
1 | import { type AuthToken, type User, type Authorizer } from '@authorizerdev/authorizer-js';
2 | import { AuthorizerProviderActionType } from '../constants';
3 | import type { Ref } from 'vue';
4 |
5 | export type AuthorizerConfig = {
6 | authorizerURL: string;
7 | redirectURL: string;
8 | client_id: string;
9 | is_google_login_enabled: boolean;
10 | is_github_login_enabled: boolean;
11 | is_facebook_login_enabled: boolean;
12 | is_linkedin_login_enabled: boolean;
13 | is_apple_login_enabled: boolean;
14 | is_twitter_login_enabled: boolean;
15 | is_microsoft_login_enabled: boolean;
16 | is_discord_login_enabled: boolean;
17 | is_roblox_login_enabled: boolean;
18 | is_email_verification_enabled: boolean;
19 | is_basic_authentication_enabled: boolean;
20 | is_magic_link_login_enabled: boolean;
21 | is_sign_up_enabled: boolean;
22 | is_strong_password_enabled: boolean;
23 | };
24 |
25 | export type AuthorizerConfigInput = {
26 | authorizerURL: string;
27 | redirectURL?: string;
28 | clientID?: string;
29 | };
30 |
31 | export type AuthorizerState = {
32 | user: User | null;
33 | token: AuthToken | null;
34 | loading: boolean;
35 | config: AuthorizerConfig;
36 | };
37 |
38 | export type AuthorizerProviderAction = {
39 | type: AuthorizerProviderActionType;
40 | // eslint-disable-next-line @typescript-eslint/no-explicit-any
41 | payload: any;
42 | };
43 |
44 | export type AuthorizerContextPropsType = {
45 | user: null | User;
46 | token: null | AuthToken;
47 | loading: boolean;
48 | logout: () => Promise;
49 | setLoading: (data: boolean) => void;
50 | setUser: (data: null | User) => void;
51 | setToken: (data: null | AuthToken) => void;
52 | setAuthData: (data: AuthorizerState) => void;
53 | authorizerRef: Authorizer;
54 | };
55 |
56 | export type AuthorizerContextOutputType = {
57 | user: Ref;
58 | token: Ref;
59 | loading: Ref;
60 | logout: Ref<() => Promise>;
61 | setLoading: Ref<(data: boolean) => void>;
62 | setUser: Ref<(data: null | User) => void>;
63 | setToken: Ref<(data: null | AuthToken) => void>;
64 | setAuthData: Ref<(data: AuthorizerState) => void>;
65 | authorizerRef: Ref;
66 | config?: {
67 | authorizerURL: Ref;
68 | redirectURL: Ref;
69 | client_id: Ref;
70 | is_google_login_enabled: Ref;
71 | is_github_login_enabled: Ref;
72 | is_facebook_login_enabled: Ref;
73 | is_linkedin_login_enabled: Ref;
74 | is_apple_login_enabled: Ref;
75 | is_twitter_login_enabled: Ref;
76 | is_microsoft_login_enabled: Ref;
77 | is_discord_login_enabled: Ref;
78 | is_roblox_login_enabled: Ref;
79 | is_email_verification_enabled: Ref;
80 | is_basic_authentication_enabled: Ref;
81 | is_magic_link_login_enabled: Ref;
82 | is_sign_up_enabled: Ref;
83 | is_strong_password_enabled: Ref;
84 | };
85 | };
86 |
87 | export type OtpDataType = {
88 | isScreenVisible: boolean;
89 | email: string;
90 | };
91 |
92 | export type URLPropsType = {
93 | redirectURL?: string;
94 | scope?: string[];
95 | state?: string;
96 | redirect_uri?: string;
97 | roles?: string[];
98 | };
99 |
--------------------------------------------------------------------------------
/src/utils/common.ts:
--------------------------------------------------------------------------------
1 | import { hasWindow } from './window';
2 |
3 | export const getIntervalDiff = (accessTokenExpiresAt: number) => {
4 | const expiresAt = accessTokenExpiresAt * 1000 - 300000;
5 | const currentDate = new Date();
6 |
7 | const millisecond = new Date(expiresAt).getTime() - currentDate.getTime();
8 | return millisecond;
9 | };
10 |
11 | export const getCrypto = () => {
12 | //ie 11.x uses msCrypto
13 | // eslint-disable-next-line @typescript-eslint/ban-ts-comment
14 | // @ts-expect-error
15 | return hasWindow() ? window.crypto || window.msCrypto : null;
16 | };
17 |
18 | export const createRandomString = () => {
19 | const charset = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_~.';
20 | let random = '';
21 | const crypto = getCrypto();
22 | if (crypto) {
23 | const randomValues = Array.from(crypto.getRandomValues(new Uint8Array(43)));
24 | randomValues.forEach((v: number) => (random += charset[v % charset.length]));
25 | }
26 | return random;
27 | };
28 |
29 | // eslint-disable-next-line @typescript-eslint/no-explicit-any
30 | export const createQueryParams = (params: any) => {
31 | return Object.keys(params)
32 | .filter((k) => typeof params[k] !== 'undefined')
33 | .map((k) => encodeURIComponent(k) + '=' + encodeURIComponent(params[k]))
34 | .join('&');
35 | };
36 |
37 | export const isValidEmail = (email: string) => {
38 | return String(email)
39 | .toLowerCase()
40 | .match(
41 | /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
42 | );
43 | };
44 |
45 | export const capitalizeFirstLetter = (data: string) => {
46 | return data ? data.charAt(0).toUpperCase() + data.slice(1) : null;
47 | };
48 |
49 | export const isValidOtp = (otp: string) => {
50 | const re = /^([A-Z0-9]{6})$/;
51 | return otp && re.test(String(otp.trim()));
52 | };
53 |
54 | export const formatErrorMessage = (message: string) => {
55 | return message.replace(`[GraphQL] `, '');
56 | };
57 |
58 | export const hasSpecialChar = (char: string) => {
59 | const re = /[`!@#$%^&*()_+\-=[\]{};':"\\|,.<>/?~]/;
60 | return re.test(char);
61 | };
62 |
63 | export const validatePassword = (value = '') => {
64 | const res: {
65 | strength: string;
66 | score: number;
67 | hasSixChar: boolean;
68 | hasLowerCase: boolean;
69 | hasNumericChar: boolean;
70 | hasSpecialChar: boolean;
71 | hasUpperCase: boolean;
72 | maxThirtySixChar: boolean;
73 | } = {
74 | score: 0,
75 | strength: '',
76 | hasSixChar: false,
77 | hasLowerCase: false,
78 | hasUpperCase: false,
79 | hasNumericChar: false,
80 | hasSpecialChar: false,
81 | maxThirtySixChar: false
82 | };
83 |
84 | if (value.length >= 6) {
85 | res.score = res.score + 1;
86 | res.hasSixChar = true;
87 | }
88 |
89 | if (value.length > 0 && value.length <= 36) {
90 | res.score = res.score + 1;
91 | res.maxThirtySixChar = true;
92 | }
93 |
94 | Array.from(value).forEach((char) => {
95 | if (char >= 'A' && char <= 'Z' && !res.hasUpperCase) {
96 | res.score = res.score + 1;
97 | res.hasUpperCase = true;
98 | } else if (char >= 'a' && char <= 'z' && !res.hasLowerCase) {
99 | res.score = res.score + 1;
100 | res.hasLowerCase = true;
101 | } else if (char >= '0' && char <= '9' && !res.hasNumericChar) {
102 | res.score = res.score + 1;
103 | res.hasNumericChar = true;
104 | } else if (hasSpecialChar(char) && !res.hasSpecialChar) {
105 | res.score = res.score + 1;
106 | res.hasSpecialChar = true;
107 | }
108 | });
109 |
110 | if (res.score <= 2) {
111 | res.strength = 'Weak';
112 | } else if (res.score <= 4) {
113 | res.strength = 'Good';
114 | } else if (res.score <= 5) {
115 | res.strength = 'Strong';
116 | } else {
117 | res.strength = 'Very Strong';
118 | }
119 |
120 | const isValid = Object.values(res).every((i) => Boolean(i));
121 | return { ...res, isValid };
122 | };
123 |
--------------------------------------------------------------------------------
/src/utils/url.ts:
--------------------------------------------------------------------------------
1 | import { hasWindow } from './window';
2 |
3 | export const getSearchParams = (search = '') => {
4 | let searchPrams = search;
5 | if (!searchPrams && hasWindow()) {
6 | searchPrams = window.location.search;
7 | }
8 | const urlSearchParams = new URLSearchParams(`${searchPrams}`);
9 | const params = Object.fromEntries(urlSearchParams.entries());
10 | return params;
11 | };
12 |
--------------------------------------------------------------------------------
/src/utils/window.ts:
--------------------------------------------------------------------------------
1 | export const hasWindow = () => typeof window !== 'undefined';
2 |
--------------------------------------------------------------------------------
/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | // tsconfig.app.json
2 | {
3 | "extends": ["@vue/tsconfig/tsconfig.json", "@vue/tsconfig/tsconfig.dom.json"],
4 | "include": ["env.d.ts", "src/**/*", "src/**/*.vue", "example/**/*", "example/**/*.vue"],
5 | "exclude": ["src/**/__tests__/*", "src/**/*.cy.ts"],
6 | "compilerOptions": {
7 | "composite": true,
8 | "baseUrl": ".",
9 | "noEmit": true
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/tsconfig.build-types.json:
--------------------------------------------------------------------------------
1 | // tsconfig.build-types.json
2 | {
3 | "include": ["src/**/*"],
4 | "exclude": ["node_modules", "**/*.cy.ts", "**/*.spec.ts", "**/__tests__/**/*"]
5 | }
6 |
--------------------------------------------------------------------------------
/tsconfig.config.json:
--------------------------------------------------------------------------------
1 | // tsconfig.config.json
2 | {
3 | "extends": ["@tsconfig/node18/tsconfig.json", "@vue/tsconfig/tsconfig.json"],
4 | "include": ["vite.config.*", "vitest.config.*", "cypress.config.*"],
5 | "compilerOptions": {
6 | "composite": true,
7 | "types": ["node"],
8 | "noEmit": true
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | // tsconfig.json
2 | {
3 | "files": [],
4 | "references": [{ "path": "./tsconfig.config.json" }, { "path": "./tsconfig.app.json" }]
5 | }
6 |
--------------------------------------------------------------------------------
/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { resolve } from 'node:path';
2 | import { defineConfig } from 'vite';
3 | import Vue from '@vitejs/plugin-vue';
4 |
5 | export default defineConfig({
6 | // If our .vue files have a style, it will be compiled as a single `.css` file under /dist.
7 | plugins: [Vue()],
8 |
9 | build: {
10 | // Output compiled files to /dist.
11 | outDir: './dist',
12 | lib: {
13 | // Set the entry point (file that contains our components exported).
14 | entry: resolve(__dirname, 'src/index.ts'),
15 | // Name of the library.
16 | name: 'authorizer-vue',
17 | // We are building for CJS and ESM, use a function to rename automatically files.
18 | // Example: my-component-library.esm.js
19 | fileName: (format) => `${'@authorizerdev/authorizer-vue'}.${format}.js`
20 | },
21 | rollupOptions: {
22 | // Vue is provided by the parent project, don't compile Vue source-code inside our library.
23 | external: ['vue', '@authorizerdev/authorizer-js'],
24 | output: {
25 | globals: {
26 | vue: 'Vue',
27 | '@authorizerdev/authorizer-js': 'authorizer-js'
28 | }
29 | },
30 | watch: {
31 | include: './src/**',
32 | clearScreen: false
33 | }
34 | }
35 | }
36 | });
37 |
--------------------------------------------------------------------------------