@fullstackunicorn/auth 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/index.dep.js +0 -0
- package/index.js +2 -0
- package/modules/auth.js +134 -0
- package/package.json +42 -0
package/index.dep.js
ADDED
|
File without changes
|
package/index.js
ADDED
package/modules/auth.js
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import '@fullstackunicorn/initialize'
|
|
2
|
+
import { fsa, axios, jwt, pem } from '#@/index.dep.js'
|
|
3
|
+
|
|
4
|
+
class Auth {
|
|
5
|
+
|
|
6
|
+
constructor() {
|
|
7
|
+
|
|
8
|
+
this.isRetry = false
|
|
9
|
+
|
|
10
|
+
this.paths = {}
|
|
11
|
+
this.paths.pem = './index.crt'
|
|
12
|
+
|
|
13
|
+
this.vars = {}
|
|
14
|
+
this.vars.realm = process.env.AUTH_REALM
|
|
15
|
+
this.vars.client = process.env.AUTH_CLIENT
|
|
16
|
+
|
|
17
|
+
this.url = {}
|
|
18
|
+
this.url.verify = process.env.URL_VERIFY
|
|
19
|
+
this.url.certificate = process.env.URL_CERTIFICATE
|
|
20
|
+
this.url.issuer = process.env.URL_ISSUER
|
|
21
|
+
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
user = async (authToken, method) => {
|
|
25
|
+
this.isRetry = false
|
|
26
|
+
try {
|
|
27
|
+
const token = this.checkToken(authToken)
|
|
28
|
+
if (!token) return {}
|
|
29
|
+
const user = await this.verifyToken(token, method)
|
|
30
|
+
if (!user?.id) return {}
|
|
31
|
+
return user
|
|
32
|
+
} catch { return {} }
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
isOffline = method => method === 'GET'
|
|
36
|
+
|
|
37
|
+
verifyOffline = async (token, pemKey, options = {}) => {
|
|
38
|
+
try {
|
|
39
|
+
const decoded = jwt.verify(token, pemKey, options)
|
|
40
|
+
return { content: decoded }
|
|
41
|
+
} catch { return {} }
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
verifyOnline = async token => {
|
|
45
|
+
try {
|
|
46
|
+
const res = await axios.post(this.url.verify, { token })
|
|
47
|
+
if (!res?.data?.verified) return {}
|
|
48
|
+
const decoded = jwt.decode(token)
|
|
49
|
+
if (!decoded) return {}
|
|
50
|
+
return { content: decoded }
|
|
51
|
+
} catch { return {} }
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
isEligible = (content = {}) => {
|
|
55
|
+
if (!content?.sub || !content?.exp) return false
|
|
56
|
+
if (content.iss !== this.url.issuer) return false
|
|
57
|
+
if (!content?.aud) return false
|
|
58
|
+
const aud = F3.isArr(content.aud) ? content.aud : [content.aud]
|
|
59
|
+
if (!aud.includes(this.vars.client)) return false
|
|
60
|
+
return true
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
verifyToken = async (token, method) => {
|
|
64
|
+
|
|
65
|
+
let result = {}
|
|
66
|
+
|
|
67
|
+
try {
|
|
68
|
+
|
|
69
|
+
const isOffline = this.isOffline(method)
|
|
70
|
+
if (!isOffline) result = await this.verifyOnline(token)
|
|
71
|
+
else result = await this.verifyOffline(token, await this.getPem(token))
|
|
72
|
+
if (!result?.content?.exp && isOffline) return await this.retryVerifyToken(token, method)
|
|
73
|
+
if (!result?.content?.exp && !isOffline) return {}
|
|
74
|
+
if (this.isTokenExpired(result.content.exp)) return {}
|
|
75
|
+
if (!this.isEligible(result.content)) return {}
|
|
76
|
+
this.isRetry = false
|
|
77
|
+
return { id: result.content.sub, token }
|
|
78
|
+
|
|
79
|
+
} catch { F3.catch.throw('verify-token-failed') }
|
|
80
|
+
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
retryVerifyToken = async (token, method) => {
|
|
84
|
+
|
|
85
|
+
if (this.isRetry) return {}
|
|
86
|
+
this.isRetry = true
|
|
87
|
+
|
|
88
|
+
try {
|
|
89
|
+
await this.updatePem()
|
|
90
|
+
return await this.verifyToken(token, method)
|
|
91
|
+
} catch { F3.catch.throw('retry-verify-token-failed') }
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
isTokenExpired = expiryDate => Date.now() > expiryDate * 1000
|
|
95
|
+
|
|
96
|
+
checkToken = authToken => {
|
|
97
|
+
const isBearer = F3.isStr(authToken) && authToken.startsWith('Bearer ')
|
|
98
|
+
return isBearer ? authToken.replace('Bearer ', '') : false
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
fetchPem = async () => {
|
|
102
|
+
try { return await axios.post(this.url.certificate, { realm: this.vars.realm }) }
|
|
103
|
+
catch { return null }
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
initPem = async () => {
|
|
107
|
+
try { if (!(await fsa.exists(this.paths.pem))) await this.updatePem() }
|
|
108
|
+
catch { F3.catch.throw('init-pem-failed') }
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
updatePem = async () => {
|
|
112
|
+
try {
|
|
113
|
+
const certificate = {}
|
|
114
|
+
const pemObject = await this.fetchPem()
|
|
115
|
+
if (!pemObject?.data?.keys) F3.throw('certificate-fetch-failed')
|
|
116
|
+
pemObject.data.keys.forEach(key => certificate[key.kid] = pem(key))
|
|
117
|
+
await fsa.writeJSON(this.paths.pem, certificate)
|
|
118
|
+
return true
|
|
119
|
+
} catch { F3.catch.throw('update-pem-failed') }
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
getPem = async token => {
|
|
123
|
+
try {
|
|
124
|
+
const file = await fsa.readFile(this.paths.pem)
|
|
125
|
+
const pemObject = JSON.parse(String(file))
|
|
126
|
+
const jwtDecoded = jwt.decode(token, { complete: true })
|
|
127
|
+
return pemObject[jwtDecoded?.header?.kid]
|
|
128
|
+
} catch { F3.catch.throw('get-pem-failed') }
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const auth = new Auth()
|
|
133
|
+
await auth.initPem()
|
|
134
|
+
export { auth }
|
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "1.0.0",
|
|
3
|
+
"name": "@fullstackunicorn/auth",
|
|
4
|
+
"author": "lucanigido (https://fullstackunicorn.dev/author/lucanigido)",
|
|
5
|
+
"description": "A component to implement OAuth in your project",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"private": false,
|
|
9
|
+
"readme": "package.readme.md",
|
|
10
|
+
"main": "index.js",
|
|
11
|
+
"files": [
|
|
12
|
+
"index.js",
|
|
13
|
+
"index.dep.js",
|
|
14
|
+
"modules/"
|
|
15
|
+
],
|
|
16
|
+
"repository": {
|
|
17
|
+
"type": "git",
|
|
18
|
+
"url": "https://gitlab.com/fullstackunicorn/auth.git"
|
|
19
|
+
},
|
|
20
|
+
"keywords": [
|
|
21
|
+
"authentication",
|
|
22
|
+
"OAuth",
|
|
23
|
+
"react-native",
|
|
24
|
+
"react",
|
|
25
|
+
"fullstackunicorn"
|
|
26
|
+
],
|
|
27
|
+
"imports": {
|
|
28
|
+
"#@": "./",
|
|
29
|
+
"#@/*": "./*"
|
|
30
|
+
},
|
|
31
|
+
"scripts": {
|
|
32
|
+
"test": "exit 1",
|
|
33
|
+
"run": "chmod +x ./config.run.sh && ./config.run.sh"
|
|
34
|
+
},
|
|
35
|
+
"dependencies": {
|
|
36
|
+
"@fullstackunicorn/initialize": "^1.0.7"
|
|
37
|
+
},
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"@fullstackunicorn/cli": "^1.0.3",
|
|
40
|
+
"@fullstackunicorn/reduce": "^1.0.19"
|
|
41
|
+
}
|
|
42
|
+
}
|