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 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
+ }