auth-a-lib 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +136 -0
- package/dist/auth-a-lib.esm.js +2 -0
- package/dist/auth-a-lib.esm.js.map +1 -0
- package/dist/auth-a-lib.umd.js +2 -0
- package/dist/auth-a-lib.umd.js.map +1 -0
- package/package.json +44 -0
package/README.md
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
# Auth-A Library
|
|
2
|
+
|
|
3
|
+
JavaScript library for integrating Auth-A authentication into your web applications. Provides a simple, secure OAuth 2.0 with PKCE flow implementation.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install auth-a-lib
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick Start
|
|
12
|
+
|
|
13
|
+
```javascript
|
|
14
|
+
import { ClientApp } from 'auth-a-lib';
|
|
15
|
+
|
|
16
|
+
// Initialize the client with your Auth-A client ID
|
|
17
|
+
const authClient = new ClientApp('your-client-id');
|
|
18
|
+
|
|
19
|
+
// Initiate login (async method)
|
|
20
|
+
await authClient.login('https://your-app.com/callback');
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Usage
|
|
24
|
+
|
|
25
|
+
### 1. Initialize the Client
|
|
26
|
+
|
|
27
|
+
Create a new instance of `ClientApp` with your client ID from the [Auth-A Developer Portal](https://auth-a.vercel.app):
|
|
28
|
+
|
|
29
|
+
```javascript
|
|
30
|
+
const authClient = new ClientApp('your-client-id-here');
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### 2. Initiate Login Flow
|
|
34
|
+
|
|
35
|
+
Call the `login()` method with your callback URL. The user will be redirected to Auth-A for authentication:
|
|
36
|
+
|
|
37
|
+
```javascript
|
|
38
|
+
// The login method is async
|
|
39
|
+
await authClient.login('https://your-app.com/callback');
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
**Note:** The `login()` method will:
|
|
43
|
+
- Generate a secure PKCE code verifier and challenge
|
|
44
|
+
- Store the verifier in `sessionStorage` for later token exchange
|
|
45
|
+
- Redirect the user to Auth-A login page
|
|
46
|
+
|
|
47
|
+
### 3. Handle the Callback
|
|
48
|
+
|
|
49
|
+
After successful authentication, Auth-A will redirect back to your specified URL with an authorization code. You'll need to exchange this code for tokens using your backend.
|
|
50
|
+
|
|
51
|
+
## API Reference
|
|
52
|
+
|
|
53
|
+
### `ClientApp`
|
|
54
|
+
|
|
55
|
+
#### Constructor
|
|
56
|
+
|
|
57
|
+
```javascript
|
|
58
|
+
new ClientApp(clientId: string)
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
- **clientId** (required): Your Auth-A application client ID
|
|
62
|
+
|
|
63
|
+
#### Methods
|
|
64
|
+
|
|
65
|
+
##### `login(redirectURL: string): Promise<void>`
|
|
66
|
+
|
|
67
|
+
Initiates the OAuth 2.0 authentication flow with PKCE.
|
|
68
|
+
|
|
69
|
+
- **redirectURL** (required): The URL to redirect to after authentication
|
|
70
|
+
- **Returns**: Promise that resolves when redirect is initiated
|
|
71
|
+
- **Throws**: Error if clientId is invalid or redirectURL is not provided
|
|
72
|
+
|
|
73
|
+
**Example:**
|
|
74
|
+
```javascript
|
|
75
|
+
try {
|
|
76
|
+
await authClient.login('https://myapp.com/auth/callback');
|
|
77
|
+
} catch (error) {
|
|
78
|
+
console.error('Login failed:', error.message);
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Browser Compatibility
|
|
83
|
+
|
|
84
|
+
This library uses the Web Crypto API and requires:
|
|
85
|
+
- Chrome 37+
|
|
86
|
+
- Firefox 34+
|
|
87
|
+
- Safari 11+
|
|
88
|
+
- Edge 79+
|
|
89
|
+
|
|
90
|
+
## Security
|
|
91
|
+
|
|
92
|
+
This library implements OAuth 2.0 with PKCE (Proof Key for Code Exchange) for enhanced security:
|
|
93
|
+
- Cryptographically secure random code verifier generation
|
|
94
|
+
- SHA-256 hashing for code challenge
|
|
95
|
+
- Code verifier stored securely in sessionStorage
|
|
96
|
+
|
|
97
|
+
## Development
|
|
98
|
+
|
|
99
|
+
### Local Testing with yalc
|
|
100
|
+
|
|
101
|
+
To test this library locally in another project:
|
|
102
|
+
|
|
103
|
+
1. Build the library:
|
|
104
|
+
```bash
|
|
105
|
+
npm run build
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
2. Publish to yalc:
|
|
109
|
+
```bash
|
|
110
|
+
yalc publish
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
3. In your test project:
|
|
114
|
+
```bash
|
|
115
|
+
yalc add auth-a-lib
|
|
116
|
+
npm install
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
4. After making changes to the library:
|
|
120
|
+
```bash
|
|
121
|
+
npm run build
|
|
122
|
+
yalc push
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### Build Commands
|
|
126
|
+
|
|
127
|
+
- `npm run build` - Build production bundles
|
|
128
|
+
- `npm run dev` - Build in watch mode for development
|
|
129
|
+
|
|
130
|
+
## License
|
|
131
|
+
|
|
132
|
+
ISC
|
|
133
|
+
|
|
134
|
+
## Author
|
|
135
|
+
|
|
136
|
+
Andrew William Staines
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
class e{clientId=null;redirectUrl=null;constructor(e){this.clientId=e}#e(){return!!this.clientId}async#t(){const e=new Uint8Array(32);return crypto.getRandomValues(e),this.#r(e)}async#n(e){const t=(new TextEncoder).encode(e),r=await crypto.subtle.digest("SHA-256",t);return this.#r(new Uint8Array(r))}#r(e){return btoa(String.fromCharCode(...e)).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}#a(e){return"https://auth-a.vercel.app/oauth/login?clientId="+this.clientId+"&redirectUrl="+this.redirectUrl+"&code_challenge="+e+"&code_challenge_method=s256"}async login(e){if(!this.#e())throw new Error("Invalid clientId");if(!e)throw new Error("No valid redirectURL provided.");this.redirectUrl=e;const t=await this.#t(),r=await this.#n(t);sessionStorage.setItem("auth_a_code_verifier",t),location.href=this.#a(r)}async handleRedirect(){const e=new URLSearchParams(window.location.search).get("code");window.history.replaceState({},"",window.location.pathname);const t=sessionStorage.getItem("auth_a_code_verifier");return e?await fetch("http://localhost:5000/api/oauth/token",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({code:e,code_verifier:t,clientId:this.clientId})}).then(e=>e.json()):null}}export{e as ClientApp};
|
|
2
|
+
//# sourceMappingURL=auth-a-lib.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth-a-lib.esm.js","sources":["../src/sdk/idp.js"],"sourcesContent":["export class ClientApp {\n clientId = null;\n redirectUrl = null;\n\n constructor(clientId) {\n this.clientId = clientId;\n }\n\n #validateId() {\n return !!this.clientId;\n }\n\n async #generateCodeVerifier() {\n const array = new Uint8Array(32);\n crypto.getRandomValues(array);\n return this.#base64UrlEncode(array);\n }\n\n async #generateCodeChallenge(verifier) {\n const encoder = new TextEncoder();\n const data = encoder.encode(verifier);\n const hash = await crypto.subtle.digest('SHA-256', data);\n return this.#base64UrlEncode(new Uint8Array(hash));\n }\n\n #base64UrlEncode(buffer) {\n const base64 = btoa(String.fromCharCode(...buffer));\n return base64\n .replace(/\\+/g, '-')\n .replace(/\\//g, '_')\n .replace(/=+$/, '');\n }\n\n #getUrl(challenge) {\n return \"https://auth-a.vercel.app/oauth/login\" +\n \"?clientId=\" + this.clientId +\n \"&redirectUrl=\" + this.redirectUrl +\n \"&code_challenge=\" + challenge +\n \"&code_challenge_method=s256\";\n }\n\n async login(redirectURL) {\n if (!this.#validateId()) {\n throw new Error(\"Invalid clientId\");\n }\n\n if (!redirectURL) {\n throw new Error('No valid redirectURL provided.');\n }\n\n this.redirectUrl = redirectURL;\n\n const verifier = await this.#generateCodeVerifier();\n const challenge = await this.#generateCodeChallenge(verifier);\n\n sessionStorage.setItem('auth_a_code_verifier', verifier);\n\n location.href = this.#getUrl(challenge);\n }\n\n async handleRedirect() {\n const params = new URLSearchParams(window.location.search);\n const code = params.get('code');\n window.history.replaceState({}, \"\", window.location.pathname)\n const verifier = sessionStorage.getItem('auth_a_code_verifier');\n if (!code) {\n return null;\n }\n return await fetch(\"http://localhost:5000/api/oauth/token\",{\n method: 'POST',\n headers: {\n \"Content-Type\": \"application/json\"\n },\n body: JSON.stringify({\n code: code,\n code_verifier: verifier,\n clientId: this.clientId\n })\n }).then(response => response.json());\n }\n}\n\n"],"names":["ClientApp","clientId","redirectUrl","constructor","this","validateId","generateCodeVerifier","array","Uint8Array","crypto","getRandomValues","base64UrlEncode","generateCodeChallenge","verifier","data","TextEncoder","encode","hash","subtle","digest","buffer","btoa","String","fromCharCode","replace","getUrl","challenge","login","redirectURL","Error","sessionStorage","setItem","location","href","handleRedirect","code","URLSearchParams","window","search","get","history","replaceState","pathname","getItem","fetch","method","headers","body","JSON","stringify","code_verifier","then","response","json"],"mappings":"AAAO,MAAMA,EACTC,SAAW,KACXC,YAAc,KAEdC,WAAAA,CAAYF,GACRG,KAAKH,SAAWA,CACpB,CAEA,EAAAI,GACI,QAASD,KAAKH,QAClB,CAEA,OAAMK,GACF,MAAMC,EAAQ,IAAIC,WAAW,IAE7B,OADAC,OAAOC,gBAAgBH,GAChBH,MAAKO,EAAiBJ,EACjC,CAEA,OAAMK,CAAuBC,GACzB,MACMC,GADU,IAAIC,aACCC,OAAOH,GACtBI,QAAaR,OAAOS,OAAOC,OAAO,UAAWL,GACnD,OAAOV,MAAKO,EAAiB,IAAIH,WAAWS,GAChD,CAEA,EAAAN,CAAiBS,GAEb,OADeC,KAAKC,OAAOC,gBAAgBH,IAEtCI,QAAQ,MAAO,KACfA,QAAQ,MAAO,KACfA,QAAQ,MAAO,GACxB,CAEA,EAAAC,CAAQC,GACJ,MAAO,kDACYtB,KAAKH,SACpB,gBAAkBG,KAAKF,YACvB,mBAAqBwB,EACrB,6BACR,CAEA,WAAMC,CAAMC,GACR,IAAKxB,MAAKC,IACN,MAAM,IAAIwB,MAAM,oBAGpB,IAAKD,EACD,MAAM,IAAIC,MAAM,kCAGpBzB,KAAKF,YAAc0B,EAEnB,MAAMf,QAAiBT,MAAKE,IACtBoB,QAAkBtB,MAAKQ,EAAuBC,GAEpDiB,eAAeC,QAAQ,uBAAwBlB,GAE/CmB,SAASC,KAAO7B,MAAKqB,EAAQC,EACjC,CAEA,oBAAMQ,GACF,MACMC,EADS,IAAIC,gBAAgBC,OAAOL,SAASM,QAC/BC,IAAI,QACxBF,OAAOG,QAAQC,aAAa,CAAA,EAAI,GAAIJ,OAAOL,SAASU,UACpD,MAAM7B,EAAWiB,eAAea,QAAQ,wBACxC,OAAKR,QAGQS,MAAM,wCAAwC,CACvDC,OAAQ,OACRC,QAAS,CACL,eAAgB,oBAEpBC,KAAMC,KAAKC,UAAU,CACjBd,KAAMA,EACNe,cAAerC,EACfZ,SAAUG,KAAKH,aAEpBkD,KAAKC,GAAYA,EAASC,QAZlB,IAaf"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).AuthA={})}(this,function(e){"use strict";e.ClientApp=class{clientId=null;redirectUrl=null;constructor(e){this.clientId=e}#e(){return!!this.clientId}async#t(){const e=new Uint8Array(32);return crypto.getRandomValues(e),this.#n(e)}async#r(e){const t=(new TextEncoder).encode(e),n=await crypto.subtle.digest("SHA-256",t);return this.#n(new Uint8Array(n))}#n(e){return btoa(String.fromCharCode(...e)).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}#i(e){return"https://auth-a.vercel.app/oauth/login?clientId="+this.clientId+"&redirectUrl="+this.redirectUrl+"&code_challenge="+e+"&code_challenge_method=s256"}async login(e){if(!this.#e())throw new Error("Invalid clientId");if(!e)throw new Error("No valid redirectURL provided.");this.redirectUrl=e;const t=await this.#t(),n=await this.#r(t);sessionStorage.setItem("auth_a_code_verifier",t),location.href=this.#i(n)}async handleRedirect(){const e=new URLSearchParams(window.location.search).get("code");window.history.replaceState({},"",window.location.pathname);const t=sessionStorage.getItem("auth_a_code_verifier");return e?await fetch("http://localhost:5000/api/oauth/token",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({code:e,code_verifier:t,clientId:this.clientId})}).then(e=>e.json()):null}}});
|
|
2
|
+
//# sourceMappingURL=auth-a-lib.umd.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth-a-lib.umd.js","sources":["../src/sdk/idp.js"],"sourcesContent":["export class ClientApp {\n clientId = null;\n redirectUrl = null;\n\n constructor(clientId) {\n this.clientId = clientId;\n }\n\n #validateId() {\n return !!this.clientId;\n }\n\n async #generateCodeVerifier() {\n const array = new Uint8Array(32);\n crypto.getRandomValues(array);\n return this.#base64UrlEncode(array);\n }\n\n async #generateCodeChallenge(verifier) {\n const encoder = new TextEncoder();\n const data = encoder.encode(verifier);\n const hash = await crypto.subtle.digest('SHA-256', data);\n return this.#base64UrlEncode(new Uint8Array(hash));\n }\n\n #base64UrlEncode(buffer) {\n const base64 = btoa(String.fromCharCode(...buffer));\n return base64\n .replace(/\\+/g, '-')\n .replace(/\\//g, '_')\n .replace(/=+$/, '');\n }\n\n #getUrl(challenge) {\n return \"https://auth-a.vercel.app/oauth/login\" +\n \"?clientId=\" + this.clientId +\n \"&redirectUrl=\" + this.redirectUrl +\n \"&code_challenge=\" + challenge +\n \"&code_challenge_method=s256\";\n }\n\n async login(redirectURL) {\n if (!this.#validateId()) {\n throw new Error(\"Invalid clientId\");\n }\n\n if (!redirectURL) {\n throw new Error('No valid redirectURL provided.');\n }\n\n this.redirectUrl = redirectURL;\n\n const verifier = await this.#generateCodeVerifier();\n const challenge = await this.#generateCodeChallenge(verifier);\n\n sessionStorage.setItem('auth_a_code_verifier', verifier);\n\n location.href = this.#getUrl(challenge);\n }\n\n async handleRedirect() {\n const params = new URLSearchParams(window.location.search);\n const code = params.get('code');\n window.history.replaceState({}, \"\", window.location.pathname)\n const verifier = sessionStorage.getItem('auth_a_code_verifier');\n if (!code) {\n return null;\n }\n return await fetch(\"http://localhost:5000/api/oauth/token\",{\n method: 'POST',\n headers: {\n \"Content-Type\": \"application/json\"\n },\n body: JSON.stringify({\n code: code,\n code_verifier: verifier,\n clientId: this.clientId\n })\n }).then(response => response.json());\n }\n}\n\n"],"names":["clientId","redirectUrl","constructor","this","validateId","generateCodeVerifier","array","Uint8Array","crypto","getRandomValues","base64UrlEncode","generateCodeChallenge","verifier","data","TextEncoder","encode","hash","subtle","digest","buffer","btoa","String","fromCharCode","replace","getUrl","challenge","login","redirectURL","Error","sessionStorage","setItem","location","href","handleRedirect","code","URLSearchParams","window","search","get","history","replaceState","pathname","getItem","fetch","method","headers","body","JSON","stringify","code_verifier","then","response","json"],"mappings":"wPAAO,MACHA,SAAW,KACXC,YAAc,KAEdC,WAAAA,CAAYF,GACRG,KAAKH,SAAWA,CACpB,CAEA,EAAAI,GACI,QAASD,KAAKH,QAClB,CAEA,OAAMK,GACF,MAAMC,EAAQ,IAAIC,WAAW,IAE7B,OADAC,OAAOC,gBAAgBH,GAChBH,MAAKO,EAAiBJ,EACjC,CAEA,OAAMK,CAAuBC,GACzB,MACMC,GADU,IAAIC,aACCC,OAAOH,GACtBI,QAAaR,OAAOS,OAAOC,OAAO,UAAWL,GACnD,OAAOV,MAAKO,EAAiB,IAAIH,WAAWS,GAChD,CAEA,EAAAN,CAAiBS,GAEb,OADeC,KAAKC,OAAOC,gBAAgBH,IAEtCI,QAAQ,MAAO,KACfA,QAAQ,MAAO,KACfA,QAAQ,MAAO,GACxB,CAEA,EAAAC,CAAQC,GACJ,MAAO,kDACYtB,KAAKH,SACpB,gBAAkBG,KAAKF,YACvB,mBAAqBwB,EACrB,6BACR,CAEA,WAAMC,CAAMC,GACR,IAAKxB,MAAKC,IACN,MAAM,IAAIwB,MAAM,oBAGpB,IAAKD,EACD,MAAM,IAAIC,MAAM,kCAGpBzB,KAAKF,YAAc0B,EAEnB,MAAMf,QAAiBT,MAAKE,IACtBoB,QAAkBtB,MAAKQ,EAAuBC,GAEpDiB,eAAeC,QAAQ,uBAAwBlB,GAE/CmB,SAASC,KAAO7B,MAAKqB,EAAQC,EACjC,CAEA,oBAAMQ,GACF,MACMC,EADS,IAAIC,gBAAgBC,OAAOL,SAASM,QAC/BC,IAAI,QACxBF,OAAOG,QAAQC,aAAa,CAAA,EAAI,GAAIJ,OAAOL,SAASU,UACpD,MAAM7B,EAAWiB,eAAea,QAAQ,wBACxC,OAAKR,QAGQS,MAAM,wCAAwC,CACvDC,OAAQ,OACRC,QAAS,CACL,eAAgB,oBAEpBC,KAAMC,KAAKC,UAAU,CACjBd,KAAMA,EACNe,cAAerC,EACfZ,SAAUG,KAAKH,aAEpBkD,KAAKC,GAAYA,EAASC,QAZlB,IAaf"}
|
package/package.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "auth-a-lib",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Package to Integrate with Auth-A dev portal",
|
|
5
|
+
"license": "ISC",
|
|
6
|
+
"author": "Andrew William Staines",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"main": "dist/auth-a-lib.umd.js",
|
|
9
|
+
"module": "dist/auth-a-lib.esm.js",
|
|
10
|
+
"browser": "dist/auth-a-lib.umd.js",
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"import": "./dist/auth-a-lib.esm.js",
|
|
14
|
+
"require": "./dist/auth-a-lib.umd.js"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"dist"
|
|
19
|
+
],
|
|
20
|
+
"scripts": {
|
|
21
|
+
"build": "rollup -c",
|
|
22
|
+
"dev": "rollup -c -w"
|
|
23
|
+
},
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"axios": "^1.13.5"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"@babel/core": "^7.29.0",
|
|
29
|
+
"@babel/preset-env": "^7.29.0",
|
|
30
|
+
"@rollup/plugin-babel": "^6.1.0",
|
|
31
|
+
"@rollup/plugin-commonjs": "^29.0.0",
|
|
32
|
+
"@rollup/plugin-node-resolve": "^16.0.3",
|
|
33
|
+
"@rollup/plugin-terser": "^0.4.4",
|
|
34
|
+
"nodemon": "^3.1.11",
|
|
35
|
+
"rollup": "^4.57.1"
|
|
36
|
+
},
|
|
37
|
+
"keywords": [
|
|
38
|
+
"auth",
|
|
39
|
+
"authentication",
|
|
40
|
+
"oauth",
|
|
41
|
+
"pkce",
|
|
42
|
+
"auth-a"
|
|
43
|
+
]
|
|
44
|
+
}
|