@roudanio/awesome-auth 0.1.1 → 0.1.4
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/dist/auth.d.ts +15 -1
- package/dist/awesome-auth.iife.js +1 -1
- package/dist/awesome-auth.js +260 -212
- package/dist/awesome-auth.umd.js +1 -1
- package/package.json +21 -17
- package/tests/unit/auth.test.ts +154 -0
- package/tsconfig.app.tsbuildinfo +1 -1
- package/tsconfig.node.tsbuildinfo +1 -1
- package/vitest.config.ts +9 -0
package/dist/auth.d.ts
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
import { default as EventEmitter3 } from 'eventemitter3';
|
|
2
2
|
import { JwtPayload } from 'jwt-decode';
|
|
3
|
+
export type GoogleButtonOptions = {
|
|
4
|
+
type?: 'standard' | 'icon';
|
|
5
|
+
theme?: 'outline' | 'filled_blue' | 'filled_black';
|
|
6
|
+
size?: 'small' | 'medium' | 'large';
|
|
7
|
+
text?: 'signin_with' | 'signup_with' | 'continue_with' | 'signin';
|
|
8
|
+
shape?: 'rectangular' | 'pill' | 'circle' | 'square';
|
|
9
|
+
logo_alignment?: 'left' | 'center';
|
|
10
|
+
width?: number;
|
|
11
|
+
locale?: string;
|
|
12
|
+
};
|
|
3
13
|
export interface AwesomeAuthProps {
|
|
4
14
|
googleId: string;
|
|
5
15
|
prefix?: string;
|
|
@@ -17,7 +27,7 @@ export declare enum AwesomeAuthEvent {
|
|
|
17
27
|
}
|
|
18
28
|
export declare class AwesomeAuth extends EventEmitter3 {
|
|
19
29
|
#private;
|
|
20
|
-
constructor({ googleId, prefix, root
|
|
30
|
+
constructor({ googleId, prefix, root }: AwesomeAuthProps);
|
|
21
31
|
get accessToken(): string;
|
|
22
32
|
get expiredIn(): number;
|
|
23
33
|
get isVerifying(): boolean;
|
|
@@ -30,10 +40,14 @@ export declare class AwesomeAuth extends EventEmitter3 {
|
|
|
30
40
|
store(key: string, value: unknown): Promise<void>;
|
|
31
41
|
retrieve(key: string): Promise<unknown>;
|
|
32
42
|
private initGoogleIdentity;
|
|
43
|
+
renderButton(target: HTMLElement, options?: GoogleButtonOptions): Promise<void>;
|
|
44
|
+
private ensureGoogleIdentityInitialized;
|
|
45
|
+
private handlePromptMoment;
|
|
33
46
|
private refreshCountDown;
|
|
34
47
|
private refreshToken;
|
|
35
48
|
private setAccessToken;
|
|
36
49
|
private verifyToken;
|
|
37
50
|
private onGoogleIdentityCallback;
|
|
51
|
+
private getRevokeHint;
|
|
38
52
|
}
|
|
39
53
|
export declare function getInstance(params: AwesomeAuthProps): AwesomeAuth;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var AwesomeAuth=function(
|
|
1
|
+
var AwesomeAuth=(function(I){"use strict";function L(r){return r&&r.__esModule&&Object.prototype.hasOwnProperty.call(r,"default")?r.default:r}var k={exports:{}},C;function R(){return C||(C=1,(function(r){var e=Object.prototype.hasOwnProperty,t="~";function a(){}Object.create&&(a.prototype=Object.create(null),new a().__proto__||(t=!1));function f(c,i,s){this.fn=c,this.context=i,this.once=s||!1}function p(c,i,s,o,g){if(typeof s!="function")throw new TypeError("The listener must be a function");var d=new f(s,o||c,g),l=t?t+i:i;return c._events[l]?c._events[l].fn?c._events[l]=[c._events[l],d]:c._events[l].push(d):(c._events[l]=d,c._eventsCount++),c}function w(c,i){--c._eventsCount===0?c._events=new a:delete c._events[i]}function u(){this._events=new a,this._eventsCount=0}u.prototype.eventNames=function(){var i=[],s,o;if(this._eventsCount===0)return i;for(o in s=this._events)e.call(s,o)&&i.push(t?o.slice(1):o);return Object.getOwnPropertySymbols?i.concat(Object.getOwnPropertySymbols(s)):i},u.prototype.listeners=function(i){var s=t?t+i:i,o=this._events[s];if(!o)return[];if(o.fn)return[o.fn];for(var g=0,d=o.length,l=new Array(d);g<d;g++)l[g]=o[g].fn;return l},u.prototype.listenerCount=function(i){var s=t?t+i:i,o=this._events[s];return o?o.fn?1:o.length:0},u.prototype.emit=function(i,s,o,g,d,l){var y=t?t+i:i;if(!this._events[y])return!1;var n=this._events[y],v=arguments.length,m,h;if(n.fn){switch(n.once&&this.removeListener(i,n.fn,void 0,!0),v){case 1:return n.fn.call(n.context),!0;case 2:return n.fn.call(n.context,s),!0;case 3:return n.fn.call(n.context,s,o),!0;case 4:return n.fn.call(n.context,s,o,g),!0;case 5:return n.fn.call(n.context,s,o,g,d),!0;case 6:return n.fn.call(n.context,s,o,g,d,l),!0}for(h=1,m=new Array(v-1);h<v;h++)m[h-1]=arguments[h];n.fn.apply(n.context,m)}else{var oe=n.length,O;for(h=0;h<oe;h++)switch(n[h].once&&this.removeListener(i,n[h].fn,void 0,!0),v){case 1:n[h].fn.call(n[h].context);break;case 2:n[h].fn.call(n[h].context,s);break;case 3:n[h].fn.call(n[h].context,s,o);break;case 4:n[h].fn.call(n[h].context,s,o,g);break;default:if(!m)for(O=1,m=new Array(v-1);O<v;O++)m[O-1]=arguments[O];n[h].fn.apply(n[h].context,m)}}return!0},u.prototype.on=function(i,s,o){return p(this,i,s,o,!1)},u.prototype.once=function(i,s,o){return p(this,i,s,o,!0)},u.prototype.removeListener=function(i,s,o,g){var d=t?t+i:i;if(!this._events[d])return this;if(!s)return w(this,d),this;var l=this._events[d];if(l.fn)l.fn===s&&(!g||l.once)&&(!o||l.context===o)&&w(this,d);else{for(var y=0,n=[],v=l.length;y<v;y++)(l[y].fn!==s||g&&!l[y].once||o&&l[y].context!==o)&&n.push(l[y]);n.length?this._events[d]=n.length===1?n[0]:n:w(this,d)}return this},u.prototype.removeAllListeners=function(i){var s;return i?(s=t?t+i:i,this._events[s]&&w(this,s)):(this._events=new a,this._eventsCount=0),this},u.prototype.off=u.prototype.removeListener,u.prototype.addListener=u.prototype.on,u.prefixed=t,u.EventEmitter=u,r.exports=u})(k)),k.exports}var D=R();const N=L(D);class S extends Error{}S.prototype.name="InvalidTokenError";function z(r){return decodeURIComponent(atob(r).replace(/(.)/g,(e,t)=>{let a=t.charCodeAt(0).toString(16).toUpperCase();return a.length<2&&(a="0"+a),"%"+a}))}function B(r){let e=r.replace(/-/g,"+").replace(/_/g,"/");switch(e.length%4){case 0:break;case 2:e+="==";break;case 3:e+="=";break;default:throw new Error("base64 string is not of the correct length")}try{return z(e)}catch{return atob(e)}}function F(r,e){if(typeof r!="string")throw new S("Invalid token specified: must be a string");e||(e={});const t=e.header===!0?0:1,a=r.split(".")[t];if(typeof a!="string")throw new S(`Invalid token specified: missing part #${t+1}`);let f;try{f=B(a)}catch(p){throw new S(`Invalid token specified: invalid base64 for part #${t+1} (${p.message})`)}try{return JSON.parse(f)}catch(p){throw new S(`Invalid token specified: invalid json for part #${t+1} (${p.message})`)}}var M=typeof global=="object"&&global&&global.Object===Object&&global,V=typeof self=="object"&&self&&self.Object===Object&&self,J=M||V||Function("return this")(),_=J.Symbol,$=Object.prototype,U=$.hasOwnProperty,q=$.toString,T=_?_.toStringTag:void 0;function H(r){var e=U.call(r,T),t=r[T];try{r[T]=void 0;var a=!0}catch{}var f=q.call(r);return a&&(e?r[T]=t:delete r[T]),f}var K=Object.prototype,Y=K.toString;function Q(r){return Y.call(r)}var W="[object Null]",X="[object Undefined]",A=_?_.toStringTag:void 0;function Z(r){return r==null?r===void 0?X:W:A&&A in Object(r)?H(r):Q(r)}var ee=Array.isArray;function te(r){return r!=null&&typeof r=="object"}var re="[object String]";function ne(r){return typeof r=="string"||!ee(r)&&te(r)&&Z(r)==re}var P=(r=>(r.INIT="init",r.VERIFYING="verifying",r.VERIFIED="verified",r.REFRESH="refresh",r.ERROR="error",r))(P||{});let j;class G extends N{#t="";#e={};#o=0;#c;#i=!1;#s=!1;#n=!1;#l=!1;#a;#h;#r;constructor({googleId:e,prefix:t="aAuth",root:a="/api"}){if(super(),this.#c=e,this.#h=t,this.#r=a,this.#a=Date.now()/1e3>>0,this.setAccessToken(localStorage.getItem(this.localKey)||"",!1),this.#t){this.verifyToken().then(f=>{f||this.initGoogleIdentity()});return}this.initGoogleIdentity()}get accessToken(){return this.#t}get expiredIn(){return this.#o?0:this.#o-this.#a}get isVerifying(){return this.#s}get isVerified(){return this.#n}get localKey(){return`${this.#h}-token`}get root(){return this.#r}get user(){return this.#e}doSignIn(){this.initGoogleIdentity()}doSignOut(){const e=this.getRevokeHint();this.setAccessToken(""),this.#e={},this.#n=!1,clearInterval(j),this.emit("verified",!1),e&&"google"in globalThis&&"accounts"in globalThis.google&&google.accounts.id.revoke(e)}async store(e,t){t=ne(t)?t:JSON.stringify(t),await fetch(`${this.#r}/store`,{method:"POST",headers:{"Content-type":"application/json",Authorization:`Bearer ${this.#t}`},body:JSON.stringify({key:e,value:t})})}async retrieve(e){const t=await fetch(`${this.#r}/retrieve`,{method:"POST",headers:{"Content-type":"application/json",Authorization:`Bearer ${this.#t}`},body:JSON.stringify({key:e})}),{data:a}=await t.json();return a?.value}initGoogleIdentity(){this.#i||(this.emit("init",!0),this.#i=!0,this.ensureGoogleIdentityInitialized().then(()=>{google.accounts.id.prompt(e=>{this.handlePromptMoment(e)})}).catch(e=>{this.#i=!1,this.emit("init",!1),this.emit("error",e.message||String(e))}))}async renderButton(e,t){if(e){try{await this.ensureGoogleIdentityInitialized()}catch(a){this.emit("error",a.message||String(a));return}if(!E()){this.emit("error","Google Identity Services 未加载完成");return}google.accounts.id.renderButton(e,{type:"standard",theme:"outline",size:"large",text:"signin_with",...t})}}async ensureGoogleIdentityInitialized(){if(await ie(),!E())throw new Error("Google Identity Services 未加载完成");this.#l||(google.accounts.id.initialize({client_id:this.#c,callback:e=>this.onGoogleIdentityCallback(e),auto_select:!0,ux_mode:"popup"}),this.#l=!0)}handlePromptMoment(e){(e.isNotDisplayed()||e.isSkippedMoment()||e.isDismissedMoment())&&(this.#i=!1,this.emit("init",!1))}refreshCountDown(){this.#a=Date.now()/1e3>>0,this.expiredIn<=60&&(clearInterval(j),this.refreshToken())}async refreshToken(){this.emit("refresh",!0);const e=await fetch(`${this.#r}/refresh-token`,{method:"POST",headers:{"Content-type":"application/json",Authorization:`Bearer ${this.#t}`}}),{data:t}=await e.json(),{token:a}=t;this.setAccessToken(a),this.emit("refresh",!1)}setAccessToken(e,t=!0){if(this.#t=e,t&&localStorage.setItem(this.localKey,e),!e){this.#e={},this.#o=0;return}try{this.#e=F(e)}catch{this.#e={}}this.#o=this.#e.exp||0,this.expiredIn>0&&(clearInterval(j),j=setInterval(()=>this.refreshCountDown(),1e3))}async verifyToken(){this.emit("verifying",!0),this.#s=!0;try{const e=await fetch(`${this.#r}/verify-auth`,{method:"POST",headers:{"Content-type":"application/json",Authorization:`Bearer ${this.#t}`}}),{data:t}=await e.json();t&&(this.#e=t,this.#n=!0,this.emit("verified",!0))}catch(e){this.#n=!1,this.emit("error",e.message||String(e))}return this.emit("verifying",!1),this.#s=!1,this.#n}async onGoogleIdentityCallback(e){this.emit("init",!1),this.#i=!1,this.emit("verifying",!0),this.#s=!0;const t=await fetch(`${this.#r}/google-auth`,{method:"POST",headers:{"Content-type":"application/json"},body:JSON.stringify({credential:e.credential})}),{data:a}=await t.json();a||this.emit("error","Failed to validate user.");const{token:f}=a;this.setAccessToken(f),this.emit("verifying",!1),this.#s=!1,this.emit("verified",!0),this.#n=!0}getRevokeHint(){const e=this.#e.email;if(typeof e=="string"&&e.trim())return e;const t=this.#e.sub;return typeof t=="string"&&t.trim()?t:""}}let b=null;function E(){return"google"in globalThis&&"accounts"in globalThis.google}function ie(){return E()?Promise.resolve():b||(b=new Promise((r,e)=>{const t="https://accounts.google.com/gsi/client",a=document.querySelector(`script[src="${t}"]`),f=a||document.createElement("script");function p(){r()}function w(){e(new Error("Google Identity Services 加载失败"))}f.addEventListener("load",p,{once:!0}),f.addEventListener("error",w,{once:!0}),a||(f.src=t,f.async=!0,document.head.appendChild(f))}),b.catch(()=>{b=null}),b)}let x;function se(r){return x=x||new G(r),x}return I.AwesomeAuth=G,I.AwesomeAuthEvent=P,I.getInstance=se,Object.defineProperty(I,Symbol.toStringTag,{value:"Module"}),I})({});
|
package/dist/awesome-auth.js
CHANGED
|
@@ -1,350 +1,398 @@
|
|
|
1
|
-
function
|
|
2
|
-
return
|
|
1
|
+
function G(r) {
|
|
2
|
+
return r && r.__esModule && Object.prototype.hasOwnProperty.call(r, "default") ? r.default : r;
|
|
3
3
|
}
|
|
4
|
-
var
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
return n.fn.call(n.context), !0;
|
|
49
|
-
case 2:
|
|
50
|
-
return n.fn.call(n.context, s), !0;
|
|
51
|
-
case 3:
|
|
52
|
-
return n.fn.call(n.context, s, o), !0;
|
|
53
|
-
case 4:
|
|
54
|
-
return n.fn.call(n.context, s, o, p), !0;
|
|
55
|
-
case 5:
|
|
56
|
-
return n.fn.call(n.context, s, o, p, u), !0;
|
|
57
|
-
case 6:
|
|
58
|
-
return n.fn.call(n.context, s, o, p, u, l), !0;
|
|
59
|
-
}
|
|
60
|
-
for (f = 1, b = new Array(y - 1); f < y; f++)
|
|
61
|
-
b[f - 1] = arguments[f];
|
|
62
|
-
n.fn.apply(n.context, b);
|
|
63
|
-
} else {
|
|
64
|
-
var C = n.length, m;
|
|
65
|
-
for (f = 0; f < C; f++)
|
|
66
|
-
switch (n[f].once && this.removeListener(i, n[f].fn, void 0, !0), y) {
|
|
4
|
+
var j = { exports: {} }, E;
|
|
5
|
+
function A() {
|
|
6
|
+
return E || (E = 1, (function(r) {
|
|
7
|
+
var e = Object.prototype.hasOwnProperty, t = "~";
|
|
8
|
+
function a() {
|
|
9
|
+
}
|
|
10
|
+
Object.create && (a.prototype = /* @__PURE__ */ Object.create(null), new a().__proto__ || (t = !1));
|
|
11
|
+
function f(c, i, s) {
|
|
12
|
+
this.fn = c, this.context = i, this.once = s || !1;
|
|
13
|
+
}
|
|
14
|
+
function p(c, i, s, o, g) {
|
|
15
|
+
if (typeof s != "function")
|
|
16
|
+
throw new TypeError("The listener must be a function");
|
|
17
|
+
var d = new f(s, o || c, g), l = t ? t + i : i;
|
|
18
|
+
return c._events[l] ? c._events[l].fn ? c._events[l] = [c._events[l], d] : c._events[l].push(d) : (c._events[l] = d, c._eventsCount++), c;
|
|
19
|
+
}
|
|
20
|
+
function b(c, i) {
|
|
21
|
+
--c._eventsCount === 0 ? c._events = new a() : delete c._events[i];
|
|
22
|
+
}
|
|
23
|
+
function u() {
|
|
24
|
+
this._events = new a(), this._eventsCount = 0;
|
|
25
|
+
}
|
|
26
|
+
u.prototype.eventNames = function() {
|
|
27
|
+
var i = [], s, o;
|
|
28
|
+
if (this._eventsCount === 0) return i;
|
|
29
|
+
for (o in s = this._events)
|
|
30
|
+
e.call(s, o) && i.push(t ? o.slice(1) : o);
|
|
31
|
+
return Object.getOwnPropertySymbols ? i.concat(Object.getOwnPropertySymbols(s)) : i;
|
|
32
|
+
}, u.prototype.listeners = function(i) {
|
|
33
|
+
var s = t ? t + i : i, o = this._events[s];
|
|
34
|
+
if (!o) return [];
|
|
35
|
+
if (o.fn) return [o.fn];
|
|
36
|
+
for (var g = 0, d = o.length, l = new Array(d); g < d; g++)
|
|
37
|
+
l[g] = o[g].fn;
|
|
38
|
+
return l;
|
|
39
|
+
}, u.prototype.listenerCount = function(i) {
|
|
40
|
+
var s = t ? t + i : i, o = this._events[s];
|
|
41
|
+
return o ? o.fn ? 1 : o.length : 0;
|
|
42
|
+
}, u.prototype.emit = function(i, s, o, g, d, l) {
|
|
43
|
+
var y = t ? t + i : i;
|
|
44
|
+
if (!this._events[y]) return !1;
|
|
45
|
+
var n = this._events[y], v = arguments.length, m, h;
|
|
46
|
+
if (n.fn) {
|
|
47
|
+
switch (n.once && this.removeListener(i, n.fn, void 0, !0), v) {
|
|
67
48
|
case 1:
|
|
68
|
-
n
|
|
69
|
-
break;
|
|
49
|
+
return n.fn.call(n.context), !0;
|
|
70
50
|
case 2:
|
|
71
|
-
n
|
|
72
|
-
break;
|
|
51
|
+
return n.fn.call(n.context, s), !0;
|
|
73
52
|
case 3:
|
|
74
|
-
n
|
|
75
|
-
break;
|
|
53
|
+
return n.fn.call(n.context, s, o), !0;
|
|
76
54
|
case 4:
|
|
77
|
-
n
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
n[f].fn.apply(n[f].context, b);
|
|
55
|
+
return n.fn.call(n.context, s, o, g), !0;
|
|
56
|
+
case 5:
|
|
57
|
+
return n.fn.call(n.context, s, o, g, d), !0;
|
|
58
|
+
case 6:
|
|
59
|
+
return n.fn.call(n.context, s, o, g, d, l), !0;
|
|
83
60
|
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
|
|
61
|
+
for (h = 1, m = new Array(v - 1); h < v; h++)
|
|
62
|
+
m[h - 1] = arguments[h];
|
|
63
|
+
n.fn.apply(n.context, m);
|
|
64
|
+
} else {
|
|
65
|
+
var P = n.length, I;
|
|
66
|
+
for (h = 0; h < P; h++)
|
|
67
|
+
switch (n[h].once && this.removeListener(i, n[h].fn, void 0, !0), v) {
|
|
68
|
+
case 1:
|
|
69
|
+
n[h].fn.call(n[h].context);
|
|
70
|
+
break;
|
|
71
|
+
case 2:
|
|
72
|
+
n[h].fn.call(n[h].context, s);
|
|
73
|
+
break;
|
|
74
|
+
case 3:
|
|
75
|
+
n[h].fn.call(n[h].context, s, o);
|
|
76
|
+
break;
|
|
77
|
+
case 4:
|
|
78
|
+
n[h].fn.call(n[h].context, s, o, g);
|
|
79
|
+
break;
|
|
80
|
+
default:
|
|
81
|
+
if (!m) for (I = 1, m = new Array(v - 1); I < v; I++)
|
|
82
|
+
m[I - 1] = arguments[I];
|
|
83
|
+
n[h].fn.apply(n[h].context, m);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return !0;
|
|
87
|
+
}, u.prototype.on = function(i, s, o) {
|
|
88
|
+
return p(this, i, s, o, !1);
|
|
89
|
+
}, u.prototype.once = function(i, s, o) {
|
|
90
|
+
return p(this, i, s, o, !0);
|
|
91
|
+
}, u.prototype.removeListener = function(i, s, o, g) {
|
|
92
|
+
var d = t ? t + i : i;
|
|
93
|
+
if (!this._events[d]) return this;
|
|
94
|
+
if (!s)
|
|
95
|
+
return b(this, d), this;
|
|
96
|
+
var l = this._events[d];
|
|
97
|
+
if (l.fn)
|
|
98
|
+
l.fn === s && (!g || l.once) && (!o || l.context === o) && b(this, d);
|
|
99
|
+
else {
|
|
100
|
+
for (var y = 0, n = [], v = l.length; y < v; y++)
|
|
101
|
+
(l[y].fn !== s || g && !l[y].once || o && l[y].context !== o) && n.push(l[y]);
|
|
102
|
+
n.length ? this._events[d] = n.length === 1 ? n[0] : n : b(this, d);
|
|
103
|
+
}
|
|
104
|
+
return this;
|
|
105
|
+
}, u.prototype.removeAllListeners = function(i) {
|
|
106
|
+
var s;
|
|
107
|
+
return i ? (s = t ? t + i : i, this._events[s] && b(this, s)) : (this._events = new a(), this._eventsCount = 0), this;
|
|
108
|
+
}, u.prototype.off = u.prototype.removeListener, u.prototype.addListener = u.prototype.on, u.prefixed = t, u.EventEmitter = u, r.exports = u;
|
|
109
|
+
})(j)), j.exports;
|
|
110
|
+
}
|
|
111
|
+
var L = A();
|
|
112
|
+
const R = /* @__PURE__ */ G(L);
|
|
111
113
|
class T extends Error {
|
|
112
114
|
}
|
|
113
115
|
T.prototype.name = "InvalidTokenError";
|
|
114
|
-
function
|
|
115
|
-
return decodeURIComponent(atob(
|
|
116
|
+
function D(r) {
|
|
117
|
+
return decodeURIComponent(atob(r).replace(/(.)/g, (e, t) => {
|
|
116
118
|
let a = t.charCodeAt(0).toString(16).toUpperCase();
|
|
117
119
|
return a.length < 2 && (a = "0" + a), "%" + a;
|
|
118
120
|
}));
|
|
119
121
|
}
|
|
120
|
-
function
|
|
121
|
-
let
|
|
122
|
-
switch (
|
|
122
|
+
function N(r) {
|
|
123
|
+
let e = r.replace(/-/g, "+").replace(/_/g, "/");
|
|
124
|
+
switch (e.length % 4) {
|
|
123
125
|
case 0:
|
|
124
126
|
break;
|
|
125
127
|
case 2:
|
|
126
|
-
|
|
128
|
+
e += "==";
|
|
127
129
|
break;
|
|
128
130
|
case 3:
|
|
129
|
-
|
|
131
|
+
e += "=";
|
|
130
132
|
break;
|
|
131
133
|
default:
|
|
132
134
|
throw new Error("base64 string is not of the correct length");
|
|
133
135
|
}
|
|
134
136
|
try {
|
|
135
|
-
return
|
|
137
|
+
return D(e);
|
|
136
138
|
} catch {
|
|
137
|
-
return atob(
|
|
139
|
+
return atob(e);
|
|
138
140
|
}
|
|
139
141
|
}
|
|
140
|
-
function
|
|
141
|
-
if (typeof
|
|
142
|
+
function z(r, e) {
|
|
143
|
+
if (typeof r != "string")
|
|
142
144
|
throw new T("Invalid token specified: must be a string");
|
|
143
|
-
|
|
144
|
-
const t =
|
|
145
|
+
e || (e = {});
|
|
146
|
+
const t = e.header === !0 ? 0 : 1, a = r.split(".")[t];
|
|
145
147
|
if (typeof a != "string")
|
|
146
148
|
throw new T(`Invalid token specified: missing part #${t + 1}`);
|
|
147
|
-
let
|
|
149
|
+
let f;
|
|
148
150
|
try {
|
|
149
|
-
|
|
150
|
-
} catch (
|
|
151
|
-
throw new T(`Invalid token specified: invalid base64 for part #${t + 1} (${
|
|
151
|
+
f = N(a);
|
|
152
|
+
} catch (p) {
|
|
153
|
+
throw new T(`Invalid token specified: invalid base64 for part #${t + 1} (${p.message})`);
|
|
152
154
|
}
|
|
153
155
|
try {
|
|
154
|
-
return JSON.parse(
|
|
155
|
-
} catch (
|
|
156
|
-
throw new T(`Invalid token specified: invalid json for part #${t + 1} (${
|
|
156
|
+
return JSON.parse(f);
|
|
157
|
+
} catch (p) {
|
|
158
|
+
throw new T(`Invalid token specified: invalid json for part #${t + 1} (${p.message})`);
|
|
157
159
|
}
|
|
158
160
|
}
|
|
159
|
-
var
|
|
160
|
-
function
|
|
161
|
-
var
|
|
161
|
+
var B = typeof global == "object" && global && global.Object === Object && global, F = typeof self == "object" && self && self.Object === Object && self, V = B || F || Function("return this")(), O = V.Symbol, $ = Object.prototype, J = $.hasOwnProperty, M = $.toString, S = O ? O.toStringTag : void 0;
|
|
162
|
+
function U(r) {
|
|
163
|
+
var e = J.call(r, S), t = r[S];
|
|
162
164
|
try {
|
|
163
|
-
|
|
165
|
+
r[S] = void 0;
|
|
164
166
|
var a = !0;
|
|
165
167
|
} catch {
|
|
166
168
|
}
|
|
167
|
-
var
|
|
168
|
-
return a && (
|
|
169
|
+
var f = M.call(r);
|
|
170
|
+
return a && (e ? r[S] = t : delete r[S]), f;
|
|
169
171
|
}
|
|
170
|
-
var
|
|
171
|
-
function
|
|
172
|
-
return
|
|
172
|
+
var q = Object.prototype, H = q.toString;
|
|
173
|
+
function K(r) {
|
|
174
|
+
return H.call(r);
|
|
173
175
|
}
|
|
174
|
-
var
|
|
175
|
-
function
|
|
176
|
-
return
|
|
176
|
+
var Y = "[object Null]", Q = "[object Undefined]", C = O ? O.toStringTag : void 0;
|
|
177
|
+
function W(r) {
|
|
178
|
+
return r == null ? r === void 0 ? Q : Y : C && C in Object(r) ? U(r) : K(r);
|
|
177
179
|
}
|
|
178
|
-
var
|
|
179
|
-
function
|
|
180
|
-
return
|
|
180
|
+
var X = Array.isArray;
|
|
181
|
+
function Z(r) {
|
|
182
|
+
return r != null && typeof r == "object";
|
|
181
183
|
}
|
|
182
|
-
var
|
|
183
|
-
function
|
|
184
|
-
return typeof
|
|
184
|
+
var ee = "[object String]";
|
|
185
|
+
function te(r) {
|
|
186
|
+
return typeof r == "string" || !X(r) && Z(r) && W(r) == ee;
|
|
185
187
|
}
|
|
186
|
-
var
|
|
188
|
+
var re = /* @__PURE__ */ ((r) => (r.INIT = "init", r.VERIFYING = "verifying", r.VERIFIED = "verified", r.REFRESH = "refresh", r.ERROR = "error", r))(re || {});
|
|
187
189
|
let _;
|
|
188
|
-
class
|
|
189
|
-
#
|
|
190
|
-
#
|
|
191
|
-
#
|
|
190
|
+
class ne extends R {
|
|
191
|
+
#t = "";
|
|
192
|
+
#e = {};
|
|
193
|
+
#o = 0;
|
|
192
194
|
#c;
|
|
193
|
-
#o = !1;
|
|
194
|
-
#n = !1;
|
|
195
195
|
#i = !1;
|
|
196
|
+
#s = !1;
|
|
197
|
+
#n = !1;
|
|
198
|
+
#l = !1;
|
|
196
199
|
#a;
|
|
197
|
-
#
|
|
200
|
+
#h;
|
|
198
201
|
#r;
|
|
199
|
-
constructor({
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
}) {
|
|
204
|
-
if (super(), this.#c = r, this.#l = t, this.#r = a, this.#a = Date.now() / 1e3 >> 0, this.setAccessToken(localStorage.getItem(this.localKey) || "", !1), this.#e) {
|
|
205
|
-
this.verifyToken().then((d) => {
|
|
206
|
-
d || this.initGoogleIdentity();
|
|
202
|
+
constructor({ googleId: e, prefix: t = "aAuth", root: a = "/api" }) {
|
|
203
|
+
if (super(), this.#c = e, this.#h = t, this.#r = a, this.#a = Date.now() / 1e3 >> 0, this.setAccessToken(localStorage.getItem(this.localKey) || "", !1), this.#t) {
|
|
204
|
+
this.verifyToken().then((f) => {
|
|
205
|
+
f || this.initGoogleIdentity();
|
|
207
206
|
});
|
|
208
207
|
return;
|
|
209
208
|
}
|
|
210
209
|
this.initGoogleIdentity();
|
|
211
210
|
}
|
|
212
211
|
get accessToken() {
|
|
213
|
-
return this.#
|
|
212
|
+
return this.#t;
|
|
214
213
|
}
|
|
215
214
|
get expiredIn() {
|
|
216
|
-
return this.#
|
|
215
|
+
return this.#o ? 0 : this.#o - this.#a;
|
|
217
216
|
}
|
|
218
217
|
get isVerifying() {
|
|
219
|
-
return this.#
|
|
218
|
+
return this.#s;
|
|
220
219
|
}
|
|
221
220
|
get isVerified() {
|
|
222
|
-
return this.#
|
|
221
|
+
return this.#n;
|
|
223
222
|
}
|
|
224
223
|
get localKey() {
|
|
225
|
-
return `${this.#
|
|
224
|
+
return `${this.#h}-token`;
|
|
226
225
|
}
|
|
227
226
|
get root() {
|
|
228
227
|
return this.#r;
|
|
229
228
|
}
|
|
230
229
|
get user() {
|
|
231
|
-
return this.#
|
|
230
|
+
return this.#e;
|
|
232
231
|
}
|
|
233
232
|
doSignIn() {
|
|
234
233
|
this.initGoogleIdentity();
|
|
235
234
|
}
|
|
236
235
|
doSignOut() {
|
|
237
|
-
|
|
236
|
+
const e = this.getRevokeHint();
|
|
237
|
+
this.setAccessToken(""), this.#e = {}, this.#n = !1, clearInterval(_), this.emit("verified", !1), e && "google" in globalThis && "accounts" in globalThis.google && google.accounts.id.revoke(e);
|
|
238
238
|
}
|
|
239
|
-
async store(
|
|
240
|
-
t =
|
|
239
|
+
async store(e, t) {
|
|
240
|
+
t = te(t) ? t : JSON.stringify(t), await fetch(`${this.#r}/store`, {
|
|
241
241
|
method: "POST",
|
|
242
242
|
headers: {
|
|
243
243
|
"Content-type": "application/json",
|
|
244
|
-
Authorization: `Bearer ${this.#
|
|
244
|
+
Authorization: `Bearer ${this.#t}`
|
|
245
245
|
},
|
|
246
246
|
body: JSON.stringify({
|
|
247
|
-
key:
|
|
247
|
+
key: e,
|
|
248
248
|
value: t
|
|
249
249
|
})
|
|
250
250
|
});
|
|
251
251
|
}
|
|
252
|
-
async retrieve(
|
|
252
|
+
async retrieve(e) {
|
|
253
253
|
const t = await fetch(`${this.#r}/retrieve`, {
|
|
254
254
|
method: "POST",
|
|
255
255
|
headers: {
|
|
256
256
|
"Content-type": "application/json",
|
|
257
|
-
Authorization: `Bearer ${this.#
|
|
257
|
+
Authorization: `Bearer ${this.#t}`
|
|
258
258
|
},
|
|
259
259
|
body: JSON.stringify({
|
|
260
|
-
key:
|
|
260
|
+
key: e
|
|
261
261
|
})
|
|
262
262
|
}), { data: a } = await t.json();
|
|
263
263
|
return a?.value;
|
|
264
264
|
}
|
|
265
265
|
initGoogleIdentity() {
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
266
|
+
this.#i || (this.emit("init", !0), this.#i = !0, this.ensureGoogleIdentityInitialized().then(() => {
|
|
267
|
+
google.accounts.id.prompt((e) => {
|
|
268
|
+
this.handlePromptMoment(e);
|
|
269
|
+
});
|
|
270
|
+
}).catch((e) => {
|
|
271
|
+
this.#i = !1, this.emit("init", !1), this.emit("error", e.message || String(e));
|
|
272
|
+
}));
|
|
273
|
+
}
|
|
274
|
+
async renderButton(e, t) {
|
|
275
|
+
if (e) {
|
|
276
|
+
try {
|
|
277
|
+
await this.ensureGoogleIdentityInitialized();
|
|
278
|
+
} catch (a) {
|
|
279
|
+
this.emit("error", a.message || String(a));
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
if (!x()) {
|
|
283
|
+
this.emit("error", "Google Identity Services 未加载完成");
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
google.accounts.id.renderButton(e, {
|
|
287
|
+
type: "standard",
|
|
288
|
+
theme: "outline",
|
|
289
|
+
size: "large",
|
|
290
|
+
text: "signin_with",
|
|
291
|
+
...t
|
|
292
|
+
});
|
|
269
293
|
}
|
|
270
|
-
|
|
294
|
+
}
|
|
295
|
+
async ensureGoogleIdentityInitialized() {
|
|
296
|
+
if (await ie(), !x())
|
|
297
|
+
throw new Error("Google Identity Services 未加载完成");
|
|
298
|
+
this.#l || (google.accounts.id.initialize({
|
|
271
299
|
client_id: this.#c,
|
|
272
|
-
callback: (
|
|
300
|
+
callback: (e) => this.onGoogleIdentityCallback(e),
|
|
273
301
|
auto_select: !0,
|
|
274
302
|
ux_mode: "popup"
|
|
275
|
-
}),
|
|
303
|
+
}), this.#l = !0);
|
|
304
|
+
}
|
|
305
|
+
handlePromptMoment(e) {
|
|
306
|
+
(e.isNotDisplayed() || e.isSkippedMoment() || e.isDismissedMoment()) && (this.#i = !1, this.emit("init", !1));
|
|
276
307
|
}
|
|
277
308
|
refreshCountDown() {
|
|
278
309
|
this.#a = Date.now() / 1e3 >> 0, this.expiredIn <= 60 && (clearInterval(_), this.refreshToken());
|
|
279
310
|
}
|
|
280
311
|
async refreshToken() {
|
|
281
312
|
this.emit("refresh", !0);
|
|
282
|
-
const
|
|
313
|
+
const e = await fetch(`${this.#r}/refresh-token`, {
|
|
283
314
|
method: "POST",
|
|
284
315
|
headers: {
|
|
285
316
|
"Content-type": "application/json",
|
|
286
|
-
Authorization: `Bearer ${this.#
|
|
317
|
+
Authorization: `Bearer ${this.#t}`
|
|
287
318
|
}
|
|
288
|
-
}), { data: t } = await
|
|
319
|
+
}), { data: t } = await e.json(), { token: a } = t;
|
|
289
320
|
this.setAccessToken(a), this.emit("refresh", !1);
|
|
290
321
|
}
|
|
291
|
-
setAccessToken(
|
|
292
|
-
if (this.#
|
|
293
|
-
this.#
|
|
322
|
+
setAccessToken(e, t = !0) {
|
|
323
|
+
if (this.#t = e, t && localStorage.setItem(this.localKey, e), !e) {
|
|
324
|
+
this.#e = {}, this.#o = 0;
|
|
294
325
|
return;
|
|
295
326
|
}
|
|
296
327
|
try {
|
|
297
|
-
this.#
|
|
328
|
+
this.#e = z(e);
|
|
298
329
|
} catch {
|
|
299
|
-
this.#
|
|
330
|
+
this.#e = {};
|
|
300
331
|
}
|
|
301
|
-
this.#
|
|
332
|
+
this.#o = this.#e.exp || 0, this.expiredIn > 0 && (clearInterval(_), _ = setInterval(() => this.refreshCountDown(), 1e3));
|
|
302
333
|
}
|
|
303
334
|
async verifyToken() {
|
|
304
|
-
this.emit("verifying", !0), this.#
|
|
335
|
+
this.emit("verifying", !0), this.#s = !0;
|
|
305
336
|
try {
|
|
306
|
-
const
|
|
337
|
+
const e = await fetch(`${this.#r}/verify-auth`, {
|
|
307
338
|
method: "POST",
|
|
308
339
|
headers: {
|
|
309
340
|
"Content-type": "application/json",
|
|
310
|
-
Authorization: `Bearer ${this.#
|
|
341
|
+
Authorization: `Bearer ${this.#t}`
|
|
311
342
|
}
|
|
312
|
-
}), { data: t } = await
|
|
313
|
-
t && (this.#
|
|
314
|
-
} catch (
|
|
315
|
-
this.#
|
|
343
|
+
}), { data: t } = await e.json();
|
|
344
|
+
t && (this.#e = t, this.#n = !0, this.emit("verified", !0));
|
|
345
|
+
} catch (e) {
|
|
346
|
+
this.#n = !1, this.emit("error", e.message || String(e));
|
|
316
347
|
}
|
|
317
|
-
return this.emit("verifying", !1), this.#
|
|
348
|
+
return this.emit("verifying", !1), this.#s = !1, this.#n;
|
|
318
349
|
}
|
|
319
|
-
async onGoogleIdentityCallback(
|
|
320
|
-
this.emit("init", !1), this.#
|
|
350
|
+
async onGoogleIdentityCallback(e) {
|
|
351
|
+
this.emit("init", !1), this.#i = !1, this.emit("verifying", !0), this.#s = !0;
|
|
321
352
|
const t = await fetch(`${this.#r}/google-auth`, {
|
|
322
353
|
method: "POST",
|
|
323
354
|
headers: {
|
|
324
355
|
"Content-type": "application/json"
|
|
325
356
|
},
|
|
326
357
|
body: JSON.stringify({
|
|
327
|
-
credential:
|
|
358
|
+
credential: e.credential
|
|
328
359
|
})
|
|
329
360
|
}), { data: a } = await t.json();
|
|
330
361
|
a || this.emit("error", "Failed to validate user.");
|
|
331
|
-
const { token:
|
|
332
|
-
this.setAccessToken(
|
|
362
|
+
const { token: f } = a;
|
|
363
|
+
this.setAccessToken(f), this.emit("verifying", !1), this.#s = !1, this.emit("verified", !0), this.#n = !0;
|
|
333
364
|
}
|
|
365
|
+
getRevokeHint() {
|
|
366
|
+
const e = this.#e.email;
|
|
367
|
+
if (typeof e == "string" && e.trim()) return e;
|
|
368
|
+
const t = this.#e.sub;
|
|
369
|
+
return typeof t == "string" && t.trim() ? t : "";
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
let w = null;
|
|
373
|
+
function x() {
|
|
374
|
+
return "google" in globalThis && "accounts" in globalThis.google;
|
|
334
375
|
}
|
|
335
|
-
function
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
376
|
+
function ie() {
|
|
377
|
+
return x() ? Promise.resolve() : w || (w = new Promise((r, e) => {
|
|
378
|
+
const t = "https://accounts.google.com/gsi/client", a = document.querySelector(`script[src="${t}"]`), f = a || document.createElement("script");
|
|
379
|
+
function p() {
|
|
380
|
+
r();
|
|
381
|
+
}
|
|
382
|
+
function b() {
|
|
383
|
+
e(new Error("Google Identity Services 加载失败"));
|
|
384
|
+
}
|
|
385
|
+
f.addEventListener("load", p, { once: !0 }), f.addEventListener("error", b, { once: !0 }), a || (f.src = t, f.async = !0, document.head.appendChild(f));
|
|
386
|
+
}), w.catch(() => {
|
|
387
|
+
w = null;
|
|
388
|
+
}), w);
|
|
341
389
|
}
|
|
342
|
-
let
|
|
343
|
-
function
|
|
344
|
-
return
|
|
390
|
+
let k;
|
|
391
|
+
function se(r) {
|
|
392
|
+
return k = k || new ne(r), k;
|
|
345
393
|
}
|
|
346
394
|
export {
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
395
|
+
ne as AwesomeAuth,
|
|
396
|
+
re as AwesomeAuthEvent,
|
|
397
|
+
se as getInstance
|
|
350
398
|
};
|
package/dist/awesome-auth.umd.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
(function(
|
|
1
|
+
(function(v,S){typeof exports=="object"&&typeof module<"u"?S(exports):typeof define=="function"&&define.amd?define(["exports"],S):(v=typeof globalThis<"u"?globalThis:v||self,S(v.AwesomeAuth={}))})(this,(function(v){"use strict";function S(n){return n&&n.__esModule&&Object.prototype.hasOwnProperty.call(n,"default")?n.default:n}var E={exports:{}},$;function R(){return $||($=1,(function(n){var e=Object.prototype.hasOwnProperty,t="~";function a(){}Object.create&&(a.prototype=Object.create(null),new a().__proto__||(t=!1));function h(c,i,s){this.fn=c,this.context=i,this.once=s||!1}function p(c,i,s,o,g){if(typeof s!="function")throw new TypeError("The listener must be a function");var d=new h(s,o||c,g),l=t?t+i:i;return c._events[l]?c._events[l].fn?c._events[l]=[c._events[l],d]:c._events[l].push(d):(c._events[l]=d,c._eventsCount++),c}function I(c,i){--c._eventsCount===0?c._events=new a:delete c._events[i]}function u(){this._events=new a,this._eventsCount=0}u.prototype.eventNames=function(){var i=[],s,o;if(this._eventsCount===0)return i;for(o in s=this._events)e.call(s,o)&&i.push(t?o.slice(1):o);return Object.getOwnPropertySymbols?i.concat(Object.getOwnPropertySymbols(s)):i},u.prototype.listeners=function(i){var s=t?t+i:i,o=this._events[s];if(!o)return[];if(o.fn)return[o.fn];for(var g=0,d=o.length,l=new Array(d);g<d;g++)l[g]=o[g].fn;return l},u.prototype.listenerCount=function(i){var s=t?t+i:i,o=this._events[s];return o?o.fn?1:o.length:0},u.prototype.emit=function(i,s,o,g,d,l){var y=t?t+i:i;if(!this._events[y])return!1;var r=this._events[y],m=arguments.length,b,f;if(r.fn){switch(r.once&&this.removeListener(i,r.fn,void 0,!0),m){case 1:return r.fn.call(r.context),!0;case 2:return r.fn.call(r.context,s),!0;case 3:return r.fn.call(r.context,s,o),!0;case 4:return r.fn.call(r.context,s,o,g),!0;case 5:return r.fn.call(r.context,s,o,g,d),!0;case 6:return r.fn.call(r.context,s,o,g,d,l),!0}for(f=1,b=new Array(m-1);f<m;f++)b[f-1]=arguments[f];r.fn.apply(r.context,b)}else{var oe=r.length,_;for(f=0;f<oe;f++)switch(r[f].once&&this.removeListener(i,r[f].fn,void 0,!0),m){case 1:r[f].fn.call(r[f].context);break;case 2:r[f].fn.call(r[f].context,s);break;case 3:r[f].fn.call(r[f].context,s,o);break;case 4:r[f].fn.call(r[f].context,s,o,g);break;default:if(!b)for(_=1,b=new Array(m-1);_<m;_++)b[_-1]=arguments[_];r[f].fn.apply(r[f].context,b)}}return!0},u.prototype.on=function(i,s,o){return p(this,i,s,o,!1)},u.prototype.once=function(i,s,o){return p(this,i,s,o,!0)},u.prototype.removeListener=function(i,s,o,g){var d=t?t+i:i;if(!this._events[d])return this;if(!s)return I(this,d),this;var l=this._events[d];if(l.fn)l.fn===s&&(!g||l.once)&&(!o||l.context===o)&&I(this,d);else{for(var y=0,r=[],m=l.length;y<m;y++)(l[y].fn!==s||g&&!l[y].once||o&&l[y].context!==o)&&r.push(l[y]);r.length?this._events[d]=r.length===1?r[0]:r:I(this,d)}return this},u.prototype.removeAllListeners=function(i){var s;return i?(s=t?t+i:i,this._events[s]&&I(this,s)):(this._events=new a,this._eventsCount=0),this},u.prototype.off=u.prototype.removeListener,u.prototype.addListener=u.prototype.on,u.prefixed=t,u.EventEmitter=u,n.exports=u})(E)),E.exports}var D=R();const N=S(D);class T extends Error{}T.prototype.name="InvalidTokenError";function z(n){return decodeURIComponent(atob(n).replace(/(.)/g,(e,t)=>{let a=t.charCodeAt(0).toString(16).toUpperCase();return a.length<2&&(a="0"+a),"%"+a}))}function B(n){let e=n.replace(/-/g,"+").replace(/_/g,"/");switch(e.length%4){case 0:break;case 2:e+="==";break;case 3:e+="=";break;default:throw new Error("base64 string is not of the correct length")}try{return z(e)}catch{return atob(e)}}function F(n,e){if(typeof n!="string")throw new T("Invalid token specified: must be a string");e||(e={});const t=e.header===!0?0:1,a=n.split(".")[t];if(typeof a!="string")throw new T(`Invalid token specified: missing part #${t+1}`);let h;try{h=B(a)}catch(p){throw new T(`Invalid token specified: invalid base64 for part #${t+1} (${p.message})`)}try{return JSON.parse(h)}catch(p){throw new T(`Invalid token specified: invalid json for part #${t+1} (${p.message})`)}}var M=typeof global=="object"&&global&&global.Object===Object&&global,V=typeof self=="object"&&self&&self.Object===Object&&self,J=M||V||Function("return this")(),j=J.Symbol,A=Object.prototype,U=A.hasOwnProperty,q=A.toString,O=j?j.toStringTag:void 0;function H(n){var e=U.call(n,O),t=n[O];try{n[O]=void 0;var a=!0}catch{}var h=q.call(n);return a&&(e?n[O]=t:delete n[O]),h}var K=Object.prototype,Y=K.toString;function Q(n){return Y.call(n)}var W="[object Null]",X="[object Undefined]",P=j?j.toStringTag:void 0;function Z(n){return n==null?n===void 0?X:W:P&&P in Object(n)?H(n):Q(n)}var ee=Array.isArray;function te(n){return n!=null&&typeof n=="object"}var ne="[object String]";function re(n){return typeof n=="string"||!ee(n)&&te(n)&&Z(n)==ne}var G=(n=>(n.INIT="init",n.VERIFYING="verifying",n.VERIFIED="verified",n.REFRESH="refresh",n.ERROR="error",n))(G||{});let k;class L extends N{#t="";#e={};#o=0;#c;#i=!1;#s=!1;#r=!1;#l=!1;#a;#f;#n;constructor({googleId:e,prefix:t="aAuth",root:a="/api"}){if(super(),this.#c=e,this.#f=t,this.#n=a,this.#a=Date.now()/1e3>>0,this.setAccessToken(localStorage.getItem(this.localKey)||"",!1),this.#t){this.verifyToken().then(h=>{h||this.initGoogleIdentity()});return}this.initGoogleIdentity()}get accessToken(){return this.#t}get expiredIn(){return this.#o?0:this.#o-this.#a}get isVerifying(){return this.#s}get isVerified(){return this.#r}get localKey(){return`${this.#f}-token`}get root(){return this.#n}get user(){return this.#e}doSignIn(){this.initGoogleIdentity()}doSignOut(){const e=this.getRevokeHint();this.setAccessToken(""),this.#e={},this.#r=!1,clearInterval(k),this.emit("verified",!1),e&&"google"in globalThis&&"accounts"in globalThis.google&&google.accounts.id.revoke(e)}async store(e,t){t=re(t)?t:JSON.stringify(t),await fetch(`${this.#n}/store`,{method:"POST",headers:{"Content-type":"application/json",Authorization:`Bearer ${this.#t}`},body:JSON.stringify({key:e,value:t})})}async retrieve(e){const t=await fetch(`${this.#n}/retrieve`,{method:"POST",headers:{"Content-type":"application/json",Authorization:`Bearer ${this.#t}`},body:JSON.stringify({key:e})}),{data:a}=await t.json();return a?.value}initGoogleIdentity(){this.#i||(this.emit("init",!0),this.#i=!0,this.ensureGoogleIdentityInitialized().then(()=>{google.accounts.id.prompt(e=>{this.handlePromptMoment(e)})}).catch(e=>{this.#i=!1,this.emit("init",!1),this.emit("error",e.message||String(e))}))}async renderButton(e,t){if(e){try{await this.ensureGoogleIdentityInitialized()}catch(a){this.emit("error",a.message||String(a));return}if(!x()){this.emit("error","Google Identity Services 未加载完成");return}google.accounts.id.renderButton(e,{type:"standard",theme:"outline",size:"large",text:"signin_with",...t})}}async ensureGoogleIdentityInitialized(){if(await ie(),!x())throw new Error("Google Identity Services 未加载完成");this.#l||(google.accounts.id.initialize({client_id:this.#c,callback:e=>this.onGoogleIdentityCallback(e),auto_select:!0,ux_mode:"popup"}),this.#l=!0)}handlePromptMoment(e){(e.isNotDisplayed()||e.isSkippedMoment()||e.isDismissedMoment())&&(this.#i=!1,this.emit("init",!1))}refreshCountDown(){this.#a=Date.now()/1e3>>0,this.expiredIn<=60&&(clearInterval(k),this.refreshToken())}async refreshToken(){this.emit("refresh",!0);const e=await fetch(`${this.#n}/refresh-token`,{method:"POST",headers:{"Content-type":"application/json",Authorization:`Bearer ${this.#t}`}}),{data:t}=await e.json(),{token:a}=t;this.setAccessToken(a),this.emit("refresh",!1)}setAccessToken(e,t=!0){if(this.#t=e,t&&localStorage.setItem(this.localKey,e),!e){this.#e={},this.#o=0;return}try{this.#e=F(e)}catch{this.#e={}}this.#o=this.#e.exp||0,this.expiredIn>0&&(clearInterval(k),k=setInterval(()=>this.refreshCountDown(),1e3))}async verifyToken(){this.emit("verifying",!0),this.#s=!0;try{const e=await fetch(`${this.#n}/verify-auth`,{method:"POST",headers:{"Content-type":"application/json",Authorization:`Bearer ${this.#t}`}}),{data:t}=await e.json();t&&(this.#e=t,this.#r=!0,this.emit("verified",!0))}catch(e){this.#r=!1,this.emit("error",e.message||String(e))}return this.emit("verifying",!1),this.#s=!1,this.#r}async onGoogleIdentityCallback(e){this.emit("init",!1),this.#i=!1,this.emit("verifying",!0),this.#s=!0;const t=await fetch(`${this.#n}/google-auth`,{method:"POST",headers:{"Content-type":"application/json"},body:JSON.stringify({credential:e.credential})}),{data:a}=await t.json();a||this.emit("error","Failed to validate user.");const{token:h}=a;this.setAccessToken(h),this.emit("verifying",!1),this.#s=!1,this.emit("verified",!0),this.#r=!0}getRevokeHint(){const e=this.#e.email;if(typeof e=="string"&&e.trim())return e;const t=this.#e.sub;return typeof t=="string"&&t.trim()?t:""}}let w=null;function x(){return"google"in globalThis&&"accounts"in globalThis.google}function ie(){return x()?Promise.resolve():w||(w=new Promise((n,e)=>{const t="https://accounts.google.com/gsi/client",a=document.querySelector(`script[src="${t}"]`),h=a||document.createElement("script");function p(){n()}function I(){e(new Error("Google Identity Services 加载失败"))}h.addEventListener("load",p,{once:!0}),h.addEventListener("error",I,{once:!0}),a||(h.src=t,h.async=!0,document.head.appendChild(h))}),w.catch(()=>{w=null}),w)}let C;function se(n){return C=C||new L(n),C}v.AwesomeAuth=L,v.AwesomeAuthEvent=G,v.getInstance=se,Object.defineProperty(v,Symbol.toStringTag,{value:"Module"})}));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@roudanio/awesome-auth",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/awesome-auth.umd.js",
|
|
6
6
|
"module": "dist/awesome-auth.js",
|
|
@@ -8,29 +8,33 @@
|
|
|
8
8
|
"types": "dist/auth.d.ts",
|
|
9
9
|
"dependencies": {
|
|
10
10
|
"eventemitter3": "^5.0.1",
|
|
11
|
-
"google-auth-library": "^
|
|
12
|
-
"jsonwebtoken": "^9.0.
|
|
11
|
+
"google-auth-library": "^10.5.0",
|
|
12
|
+
"jsonwebtoken": "^9.0.3",
|
|
13
13
|
"jwt-decode": "^4.0.0",
|
|
14
|
-
"lodash-es": "^4.17.
|
|
15
|
-
"vue": "^3.5.
|
|
16
|
-
"@awesome-comment/core": "0.1.
|
|
14
|
+
"lodash-es": "^4.17.22",
|
|
15
|
+
"vue": "^3.5.26",
|
|
16
|
+
"@awesome-comment/core": "0.1.1"
|
|
17
17
|
},
|
|
18
18
|
"devDependencies": {
|
|
19
19
|
"@types/gapi": "^0.0.47",
|
|
20
20
|
"@types/gapi.auth2": "^0.0.61",
|
|
21
|
-
"@types/google.accounts": "^0.0.
|
|
22
|
-
"@types/jsonwebtoken": "^9.0.
|
|
23
|
-
"@
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
"
|
|
27
|
-
"
|
|
28
|
-
"
|
|
29
|
-
"vite
|
|
30
|
-
"
|
|
21
|
+
"@types/google.accounts": "^0.0.18",
|
|
22
|
+
"@types/jsonwebtoken": "^9.0.10",
|
|
23
|
+
"@types/lodash-es": "^4.17.12",
|
|
24
|
+
"@vitejs/plugin-vue": "^6.0.3",
|
|
25
|
+
"autoprefixer": "^10.4.23",
|
|
26
|
+
"postcss": "^8.5.6",
|
|
27
|
+
"tailwindcss": "^3.4.19",
|
|
28
|
+
"typescript": "^5.9.3",
|
|
29
|
+
"vite": "^7.3.1",
|
|
30
|
+
"vite-plugin-dts": "^4.5.4",
|
|
31
|
+
"vitest": "^4.0.17",
|
|
32
|
+
"vue-tsc": "^3.2.2"
|
|
31
33
|
},
|
|
32
34
|
"scripts": {
|
|
33
35
|
"dev": "vite",
|
|
34
|
-
"build": "vue-tsc -b && vite build"
|
|
36
|
+
"build": "vue-tsc -b && vite build",
|
|
37
|
+
"test": "vitest",
|
|
38
|
+
"test:run": "vitest run"
|
|
35
39
|
}
|
|
36
40
|
}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
+
import { AwesomeAuth, AwesomeAuthEvent } from '../../src/auth';
|
|
3
|
+
|
|
4
|
+
type PromptCallback = (notification: google.accounts.id.PromptMomentNotification) => void;
|
|
5
|
+
|
|
6
|
+
type PromptState = 'not-displayed' | 'skipped' | 'dismissed' | 'displayed';
|
|
7
|
+
|
|
8
|
+
function createPromptNotification(state: PromptState): google.accounts.id.PromptMomentNotification {
|
|
9
|
+
return {
|
|
10
|
+
isDisplayMoment: () => state === 'displayed' || state === 'not-displayed',
|
|
11
|
+
isDisplayed: () => state === 'displayed',
|
|
12
|
+
isNotDisplayed: () => state === 'not-displayed',
|
|
13
|
+
getNotDisplayedReason: () => 'suppressed_by_user',
|
|
14
|
+
isSkippedMoment: () => state === 'skipped',
|
|
15
|
+
getSkippedReason: () => 'user_cancel',
|
|
16
|
+
isDismissedMoment: () => state === 'dismissed',
|
|
17
|
+
getDismissedReason: () => 'cancel_called',
|
|
18
|
+
getMomentType: () => {
|
|
19
|
+
if (state === 'skipped') return 'skipped';
|
|
20
|
+
if (state === 'dismissed') return 'dismissed';
|
|
21
|
+
return 'display';
|
|
22
|
+
},
|
|
23
|
+
} as unknown as google.accounts.id.PromptMomentNotification;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
describe('AwesomeAuth One Tap', () => {
|
|
27
|
+
let promptCallbacks: PromptCallback[];
|
|
28
|
+
let initialize: ReturnType<typeof vi.fn>;
|
|
29
|
+
let prompt: ReturnType<typeof vi.fn>;
|
|
30
|
+
let revoke: ReturnType<typeof vi.fn>;
|
|
31
|
+
|
|
32
|
+
beforeEach(() => {
|
|
33
|
+
promptCallbacks = [];
|
|
34
|
+
initialize = vi.fn();
|
|
35
|
+
prompt = vi.fn((callback?: PromptCallback) => {
|
|
36
|
+
if (callback) {
|
|
37
|
+
promptCallbacks.push(callback);
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
revoke = vi.fn();
|
|
41
|
+
|
|
42
|
+
const googleMock = {
|
|
43
|
+
accounts: {
|
|
44
|
+
id: {
|
|
45
|
+
initialize,
|
|
46
|
+
prompt,
|
|
47
|
+
revoke,
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
(globalThis as typeof globalThis & { google: typeof google }).google = googleMock as unknown as typeof google;
|
|
53
|
+
|
|
54
|
+
const storage = new Map<string, string>();
|
|
55
|
+
const localStorageMock: Storage = {
|
|
56
|
+
get length() {
|
|
57
|
+
return storage.size;
|
|
58
|
+
},
|
|
59
|
+
clear() {
|
|
60
|
+
storage.clear();
|
|
61
|
+
},
|
|
62
|
+
getItem(key: string) {
|
|
63
|
+
return storage.get(key) ?? null;
|
|
64
|
+
},
|
|
65
|
+
key(index: number) {
|
|
66
|
+
return Array.from(storage.keys())[index] ?? null;
|
|
67
|
+
},
|
|
68
|
+
removeItem(key: string) {
|
|
69
|
+
storage.delete(key);
|
|
70
|
+
},
|
|
71
|
+
setItem(key: string, value: string) {
|
|
72
|
+
storage.set(key, String(value));
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
(globalThis as typeof globalThis & { localStorage: Storage }).localStorage = localStorageMock;
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
afterEach(() => {
|
|
79
|
+
delete (globalThis as typeof globalThis & { google?: typeof google }).google;
|
|
80
|
+
delete (globalThis as typeof globalThis & { localStorage?: Storage }).localStorage;
|
|
81
|
+
promptCallbacks = [];
|
|
82
|
+
vi.clearAllMocks();
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it('在提示被关闭后重置初始化状态,并允许再次唤起', async () => {
|
|
86
|
+
const events: boolean[] = [];
|
|
87
|
+
const auth = new AwesomeAuth({ googleId: 'test-client' });
|
|
88
|
+
auth.on(AwesomeAuthEvent.INIT, (value: boolean) => {
|
|
89
|
+
events.push(value);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
await flushPromises();
|
|
93
|
+
expect(promptCallbacks).toHaveLength(1);
|
|
94
|
+
promptCallbacks[0](createPromptNotification('dismissed'));
|
|
95
|
+
|
|
96
|
+
expect(events).toEqual([false]);
|
|
97
|
+
|
|
98
|
+
auth.doSignIn();
|
|
99
|
+
await flushPromises();
|
|
100
|
+
expect(promptCallbacks).toHaveLength(2);
|
|
101
|
+
expect(events[1]).toBe(true);
|
|
102
|
+
expect(initialize).toHaveBeenCalledTimes(1);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('在提示未显示时结束初始化状态', async () => {
|
|
106
|
+
const events: boolean[] = [];
|
|
107
|
+
const auth = new AwesomeAuth({ googleId: 'test-client' });
|
|
108
|
+
auth.on(AwesomeAuthEvent.INIT, (value: boolean) => {
|
|
109
|
+
events.push(value);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
await flushPromises();
|
|
113
|
+
expect(promptCallbacks).toHaveLength(1);
|
|
114
|
+
promptCallbacks[0](createPromptNotification('not-displayed'));
|
|
115
|
+
|
|
116
|
+
expect(events).toEqual([false]);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it('未提供 hint 时不调用 revoke', () => {
|
|
120
|
+
const auth = new AwesomeAuth({ googleId: 'test-client' });
|
|
121
|
+
|
|
122
|
+
auth.doSignOut();
|
|
123
|
+
|
|
124
|
+
expect(revoke).not.toHaveBeenCalled();
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it('使用 email 作为 hint 调用 revoke', () => {
|
|
128
|
+
const token = createJwt({
|
|
129
|
+
email: 'test@example.com',
|
|
130
|
+
sub: 'sub-123',
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
const auth = new AwesomeAuth({ googleId: 'test-client' });
|
|
134
|
+
(auth as unknown as { setAccessToken: (value: string, local?: boolean) => void }).setAccessToken(token, false);
|
|
135
|
+
|
|
136
|
+
auth.doSignOut();
|
|
137
|
+
|
|
138
|
+
expect(revoke).toHaveBeenCalledWith('test@example.com');
|
|
139
|
+
});
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
function createJwt(payload: Record<string, unknown>): string {
|
|
143
|
+
const header = encodeBase64Url(JSON.stringify({ alg: 'none', typ: 'JWT' }));
|
|
144
|
+
const body = encodeBase64Url(JSON.stringify(payload));
|
|
145
|
+
return `${header}.${body}.`;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function encodeBase64Url(value: string): string {
|
|
149
|
+
return Buffer.from(value, 'utf-8').toString('base64').replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/g, '');
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
async function flushPromises(): Promise<void> {
|
|
153
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
154
|
+
}
|
package/tsconfig.app.tsbuildinfo
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"root":["./src/auth.ts","./src/main.ts","./src/vite-env.d.ts","./src/app.vue"],"version":"5.
|
|
1
|
+
{"root":["./src/auth.ts","./src/main.ts","./src/vite-env.d.ts","./src/app.vue"],"version":"5.9.3"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"root":["./vite.config.ts"],"version":"5.
|
|
1
|
+
{"root":["./vite.config.ts"],"version":"5.9.3"}
|