@fullstackunicorn/create-root 1.0.28 → 1.0.29
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/assets/root.fullstackunicorn.dev/frontend/App.uri.js +10 -4
- package/assets/root.fullstackunicorn.dev/frontend/base/account/@use.js +9 -16
- package/assets/root.fullstackunicorn.dev/frontend/base/cookies/@tools.js +2 -46
- package/assets/root.fullstackunicorn.dev/frontend/base/loader/@tools.js +2 -1
- package/assets/root.fullstackunicorn.dev/frontend/client/auth.js +52 -66
- package/assets/root.fullstackunicorn.dev/frontend/configure/env.json +1 -0
- package/assets/root.fullstackunicorn.dev/frontend/configure/env.prod.json +1 -0
- package/assets/root.fullstackunicorn.dev/frontend/imports/device.js +46 -0
- package/assets/root.fullstackunicorn.dev/frontend/imports/router.js +2 -0
- package/package.json +1 -1
|
@@ -51,10 +51,16 @@ uri.errors.error = `:lang/errors/:message`
|
|
|
51
51
|
uri.auth = {}
|
|
52
52
|
const { env } = constants
|
|
53
53
|
|
|
54
|
-
uri.auth.
|
|
55
|
-
uri.auth.
|
|
56
|
-
uri.auth.
|
|
57
|
-
uri.auth.
|
|
54
|
+
uri.auth.gateway = {}
|
|
55
|
+
uri.auth.gateway.login = `${env.SERVER_URL_AUTH}/auth/login`
|
|
56
|
+
uri.auth.gateway.logout = `${env.SERVER_URL_AUTH}/auth/logout`
|
|
57
|
+
uri.auth.gateway.refresh = `${env.SERVER_URL_AUTH}/auth/refresh`
|
|
58
|
+
|
|
59
|
+
uri.auth.bff = {}
|
|
60
|
+
uri.auth.bff.login = `${env.SERVER_URL_BFF}/auth/login`
|
|
61
|
+
uri.auth.bff.logout = `${env.SERVER_URL_BFF}/auth/logout`
|
|
62
|
+
uri.auth.bff.sessionMe = `${env.SERVER_URL_BFF}/session/me`
|
|
63
|
+
uri.auth.bff.bootstrap = `${env.SERVER_URL_BFF}/bootstrap`
|
|
58
64
|
|
|
59
65
|
uri.auth.user = {}
|
|
60
66
|
uri.auth.user.login = `${env.CLIENT_URL_AUTH}/auth?:type&:token`
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { atom, useAtom, useSetAtom, useGetAtom
|
|
2
|
-
import { useNavigateUrl } from '#@/imports/router.js'
|
|
3
|
-
import {
|
|
1
|
+
import { atom, useAtom, useSetAtom, useGetAtom } from '#@/imports/npm.js'
|
|
2
|
+
import { getCurrentHref, useNavigate, useNavigateUrl } from '#@/imports/router.js'
|
|
3
|
+
import { useSetAlerts } from '#@/base/alerts/@use.js'
|
|
4
4
|
import { uri } from '#@/App.uri.js'
|
|
5
5
|
import { abs } from '#@/abstracts/abs.js'
|
|
6
6
|
import { api } from '#@/client/@api.js'
|
|
@@ -14,7 +14,7 @@ export const initializeUser = atom(
|
|
|
14
14
|
(get) => get(isUserInitialized),
|
|
15
15
|
async (get, set, value) => {
|
|
16
16
|
|
|
17
|
-
const userInit = await api.auth.
|
|
17
|
+
const userInit = await api.auth.bootstrap()
|
|
18
18
|
const userInitialized = userInit?.id ? userInit : {}
|
|
19
19
|
|
|
20
20
|
set(user, userInitialized)
|
|
@@ -38,16 +38,8 @@ export const useAuthLogin = () => {
|
|
|
38
38
|
|
|
39
39
|
if (!types.includes(type)) return
|
|
40
40
|
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
tokenRaw.request_type = type
|
|
44
|
-
tokenRaw.client_id = env.CLIENT_ID
|
|
45
|
-
tokenRaw.protocol = env.PROTOCOL_AUTH
|
|
46
|
-
|
|
47
|
-
storage.set(type, tokenRaw.nonce)
|
|
48
|
-
|
|
49
|
-
const token = abs.jwt.encode(tokenRaw)
|
|
50
|
-
const authLink = F3.fabUrl(uri.auth.user.login, { type, token })
|
|
41
|
+
const returnUrl = encodeURIComponent(getCurrentHref())
|
|
42
|
+
const authLink = `${uri.auth.bff.login}?type=${type}&client_id=${env.CLIENT_ID}&return_url=${returnUrl}`
|
|
51
43
|
|
|
52
44
|
navigateUrl(authLink)
|
|
53
45
|
}
|
|
@@ -58,6 +50,8 @@ export const useAuthLogout = () => {
|
|
|
58
50
|
|
|
59
51
|
const { lang } = useUrl()
|
|
60
52
|
const setUser = useSetUser()
|
|
53
|
+
const setAlerts = useSetAlerts()
|
|
54
|
+
const navigate = useNavigate()
|
|
61
55
|
|
|
62
56
|
const authLogout = async () => {
|
|
63
57
|
|
|
@@ -69,8 +63,7 @@ export const useAuthLogout = () => {
|
|
|
69
63
|
navigate(F3.fabUrl(uri.errors, { message, lang }))
|
|
70
64
|
}
|
|
71
65
|
else {
|
|
72
|
-
|
|
73
|
-
storage.remove('refreshToken')
|
|
66
|
+
api.auth.cleanUp()
|
|
74
67
|
setUser({})
|
|
75
68
|
}
|
|
76
69
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { constants } from '#@/root/constants.js'
|
|
2
|
+
import { getSubmitMetadata } from '#@/imports/device.js'
|
|
2
3
|
|
|
3
4
|
class Tools {
|
|
4
5
|
|
|
@@ -15,52 +16,7 @@ class Tools {
|
|
|
15
16
|
getSubmit = async (device_id) => {
|
|
16
17
|
const { env } = constants
|
|
17
18
|
|
|
18
|
-
|
|
19
|
-
const metadataObj = {
|
|
20
|
-
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
|
21
|
-
language: window.navigator.language,
|
|
22
|
-
pageTitle: document.title,
|
|
23
|
-
referrer: document.referrer,
|
|
24
|
-
pageURL: window.location.href,
|
|
25
|
-
platform: window.navigator.platform,
|
|
26
|
-
deviceMemory: navigator.deviceMemory,
|
|
27
|
-
screenWidth: window.screen.width,
|
|
28
|
-
screenHeight: window.screen.height,
|
|
29
|
-
colorDepth: window.screen.colorDepth,
|
|
30
|
-
maxScreenWidth: window.screen.width * window.devicePixelRatio,
|
|
31
|
-
maxScreenHeight: window.screen.height * window.devicePixelRatio,
|
|
32
|
-
pixelRatio: window.devicePixelRatio,
|
|
33
|
-
screenOrientation: screen?.orientation?.type,
|
|
34
|
-
hardwareConcurrency: navigator.hardwareConcurrency,
|
|
35
|
-
touchSupport: 'ontouchstart' in window || navigator.maxTouchPoints > 0,
|
|
36
|
-
colorGamut: window.matchMedia("(color-gamut: srgb)").matches
|
|
37
|
-
? "srgb"
|
|
38
|
-
: window.matchMedia("(color-gamut: p3)").matches
|
|
39
|
-
? "p3"
|
|
40
|
-
: window.matchMedia("(color-gamut: rec2020)").matches
|
|
41
|
-
? "rec2020"
|
|
42
|
-
: "unknown",
|
|
43
|
-
rtt: navigator?.connection?.rtt,
|
|
44
|
-
hasGamepad: navigator.getGamepads ? navigator.getGamepads().length > 0 : false,
|
|
45
|
-
keyboardEnabled: "onkeydown" in document,
|
|
46
|
-
cookieEnabled: navigator.cookieEnabled,
|
|
47
|
-
javaEnabled: navigator.javaEnabled ? navigator.javaEnabled() : false,
|
|
48
|
-
doNotTrack: navigator.doNotTrack,
|
|
49
|
-
networkType: navigator?.connection?.effectiveType,
|
|
50
|
-
downlinkSpeed: navigator?.connection?.downlink,
|
|
51
|
-
batteryLevel: battery?.level * 100,
|
|
52
|
-
batteryCharging: battery?.charging
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
const metadata = {
|
|
56
|
-
submit_timestamp: new Date().toISOString(),
|
|
57
|
-
submit_device_id: device_id,
|
|
58
|
-
submit_client_id: env.CLIENT_ID,
|
|
59
|
-
submit_user_agent: window.navigator.userAgent,
|
|
60
|
-
submit_metadata: Object.entries(metadataObj).map(([key, value]) => `${key}:${value ? value : 'unknown'}`).join(" | ")
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
return metadata
|
|
19
|
+
return await getSubmitMetadata({ clientId: env.CLIENT_ID, deviceId: device_id })
|
|
64
20
|
}
|
|
65
21
|
}
|
|
66
22
|
|
|
@@ -11,6 +11,7 @@ class Tools {
|
|
|
11
11
|
if (user.isOffline) setAlerts({ id: 'offline', onClose: () => setIsAuthRun(true) })
|
|
12
12
|
else if (user.isError) navigate(`/errors/${user.error || 'login-failed'}`)
|
|
13
13
|
else {
|
|
14
|
+
api.auth.cleanUp()
|
|
14
15
|
setUser(user)
|
|
15
16
|
navigate('/user')
|
|
16
17
|
}
|
|
@@ -108,4 +109,4 @@ class Tools {
|
|
|
108
109
|
}
|
|
109
110
|
}
|
|
110
111
|
|
|
111
|
-
export const tools = new Tools()
|
|
112
|
+
export const tools = new Tools()
|
|
@@ -8,9 +8,23 @@ import { storage } from '#@/imports/storage'
|
|
|
8
8
|
class Auth {
|
|
9
9
|
|
|
10
10
|
config = { headers: { 'Content-Type': 'application/json' } }
|
|
11
|
+
sessionConfig = { headers: { 'Content-Type': 'application/json' }, withCredentials: true }
|
|
11
12
|
env = constants.env
|
|
12
13
|
|
|
13
14
|
getUser = (token = {}) => ({ id: token.sub, logo: token.logo })
|
|
15
|
+
getSessionUser = (payload = {}) => ({ id: payload?.user?.id, logo: payload?.user?.logo })
|
|
16
|
+
getBootstrapUser = (payload = {}) => {
|
|
17
|
+
const user = this.getSessionUser(payload)
|
|
18
|
+
return {
|
|
19
|
+
...user,
|
|
20
|
+
bootstrap: payload?.bootstrap || {},
|
|
21
|
+
permissions: payload?.permissions || [],
|
|
22
|
+
groups: payload?.groups || [],
|
|
23
|
+
entities: payload?.entities || {},
|
|
24
|
+
client: payload?.client || null,
|
|
25
|
+
features: payload?.features || {}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
14
28
|
|
|
15
29
|
loginOps = ['login', 'signup']
|
|
16
30
|
|
|
@@ -25,17 +39,9 @@ class Auth {
|
|
|
25
39
|
|
|
26
40
|
const nonce = storage.get(type)
|
|
27
41
|
|
|
28
|
-
// const isValid = nonce === token.nonce
|
|
29
|
-
// if (!isValid) throw new Error('abort')
|
|
30
|
-
|
|
31
|
-
// const rawToken = {}
|
|
32
|
-
// rawToken.authcode = token.authcode
|
|
33
|
-
// // rawToken.nonce = nonce
|
|
34
|
-
// rawToken.client_id = env.CLIENT_ID
|
|
35
|
-
|
|
36
42
|
const config = this.config
|
|
37
43
|
const data = { token: token_encoded, nonce }
|
|
38
|
-
const endpoint = uri.auth.
|
|
44
|
+
const endpoint = uri.auth.gateway.login
|
|
39
45
|
|
|
40
46
|
const response = await client.post(endpoint, data, config)
|
|
41
47
|
|
|
@@ -50,38 +56,45 @@ class Auth {
|
|
|
50
56
|
|
|
51
57
|
return this.getUser(response.access_token)
|
|
52
58
|
|
|
53
|
-
} catch (error) {
|
|
59
|
+
} catch (error) {
|
|
54
60
|
console.log("error", error)
|
|
55
|
-
return err.cleanup(() => storage.remove(type), { isError: true })
|
|
61
|
+
return err.cleanup(() => storage.remove(type), { isError: true })
|
|
62
|
+
}
|
|
56
63
|
}
|
|
57
64
|
|
|
58
65
|
refresh = async (refresh_token) => {
|
|
59
66
|
|
|
60
67
|
const config = this.config
|
|
61
68
|
const data = { refresh_token, client_id: this.env.CLIENT_ID }
|
|
62
|
-
const endpoint = uri.auth.
|
|
69
|
+
const endpoint = uri.auth.gateway.refresh
|
|
63
70
|
|
|
64
71
|
return await client.post(endpoint, data, config)
|
|
65
72
|
|
|
66
73
|
}
|
|
67
74
|
|
|
68
75
|
logout = async () => {
|
|
76
|
+
const endpoint = uri.auth.bff.logout
|
|
77
|
+
const response = await client.post(endpoint, {}, this.sessionConfig)
|
|
78
|
+
return response?.isLogout ? response : { isLogout: true }
|
|
69
79
|
|
|
70
|
-
|
|
71
|
-
const access_token = storage.get('accessToken') || ''
|
|
72
|
-
|
|
73
|
-
if (!refresh_token && !access_token) return { isLogout: true }
|
|
74
|
-
|
|
75
|
-
const config = this.config
|
|
76
|
-
const data = { access_token, refresh_token, client_id: this.env.CLIENT_ID }
|
|
77
|
-
const endpoint = uri.auth.client.logout
|
|
78
|
-
|
|
79
|
-
const response = await client.post(endpoint, data, config)
|
|
80
|
+
}
|
|
80
81
|
|
|
81
|
-
|
|
82
|
+
bootstrap = async () => {
|
|
83
|
+
try {
|
|
84
|
+
const response = await client.data({
|
|
85
|
+
key: uri.auth.bff.bootstrap,
|
|
86
|
+
method: 'get',
|
|
87
|
+
endpoint: uri.auth.bff.bootstrap,
|
|
88
|
+
config: this.sessionConfig
|
|
89
|
+
}, true)
|
|
82
90
|
|
|
83
|
-
|
|
91
|
+
if (response.isOffline) return { isOffline: true }
|
|
92
|
+
if (response.isError) throw new Error('is-logout')
|
|
93
|
+
if (!response?.user?.id) throw new Error('is-logout')
|
|
84
94
|
|
|
95
|
+
this.cleanUp()
|
|
96
|
+
return this.getBootstrapUser(response)
|
|
97
|
+
} catch (error) { return { isLogout: true } }
|
|
85
98
|
}
|
|
86
99
|
|
|
87
100
|
handleAccessToken = (accessToken) => {
|
|
@@ -156,57 +169,30 @@ class Auth {
|
|
|
156
169
|
init = async () => {
|
|
157
170
|
|
|
158
171
|
try {
|
|
172
|
+
const bootstrapped = await this.bootstrap()
|
|
173
|
+
if (bootstrapped?.id || bootstrapped?.isOffline) return bootstrapped
|
|
159
174
|
|
|
160
|
-
const
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
175
|
+
const response = await client.data({
|
|
176
|
+
key: uri.auth.bff.sessionMe,
|
|
177
|
+
method: 'get',
|
|
178
|
+
endpoint: uri.auth.bff.sessionMe,
|
|
179
|
+
config: this.sessionConfig
|
|
180
|
+
}, true)
|
|
165
181
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
if (access.decoded?.sub) return this.getUser(access.decoded)
|
|
170
|
-
|
|
171
|
-
access = await this.handleRefreshToken(refreshToken)
|
|
172
|
-
if (access.decoded?.sub) return this.getUser(access.decoded)
|
|
173
|
-
|
|
174
|
-
if (access.isOffline) {
|
|
175
|
-
const user = this.getUserOffline(accessToken)
|
|
176
|
-
if (user.id) return user
|
|
177
|
-
new Error('is-logout')
|
|
178
|
-
}
|
|
182
|
+
if (response.isOffline) return { isOffline: true }
|
|
183
|
+
if (response.isError) throw new Error('is-logout')
|
|
184
|
+
if (!response?.user?.id) throw new Error('is-logout')
|
|
179
185
|
|
|
180
|
-
|
|
186
|
+
this.cleanUp()
|
|
187
|
+
return this.getSessionUser(response)
|
|
181
188
|
|
|
182
|
-
} catch (error) {
|
|
189
|
+
} catch (error) {
|
|
183
190
|
return { isLogout: true }
|
|
184
|
-
// return err.cleanup(this.cleanUp, { isLogout: true })
|
|
185
191
|
}
|
|
186
192
|
}
|
|
187
193
|
|
|
188
194
|
getAccessToken = async () => {
|
|
189
|
-
|
|
190
|
-
try {
|
|
191
|
-
|
|
192
|
-
const accessToken = storage.get('accessToken')
|
|
193
|
-
if (!accessToken) throw new Error('is-logout')
|
|
194
|
-
|
|
195
|
-
const refreshToken = storage.get('refreshToken')
|
|
196
|
-
if (!refreshToken) throw new Error('is-logout')
|
|
197
|
-
|
|
198
|
-
let access
|
|
199
|
-
|
|
200
|
-
access = this.handleAccessToken(accessToken)
|
|
201
|
-
if (access.token) return { accessToken: access.token }
|
|
202
|
-
|
|
203
|
-
access = await this.handleRefreshToken(refreshToken)
|
|
204
|
-
if (access.token) return { accessToken: access.token }
|
|
205
|
-
|
|
206
|
-
if (access.isOffline) return { isOffline: true }
|
|
207
|
-
throw new Error('is-logout')
|
|
208
|
-
|
|
209
|
-
} catch (error) { return err.cleanup(this.cleanUp, { isLogout: true }) }
|
|
195
|
+
return { isLogout: true }
|
|
210
196
|
}
|
|
211
197
|
}
|
|
212
198
|
|
|
@@ -4,5 +4,6 @@
|
|
|
4
4
|
"CLIENT_ID": "clientxx-xxxx-4xxx-8xxx-xxxxxxxxxxxx",
|
|
5
5
|
"CLIENT_URL_AUTH": "https://auth.minderable.com",
|
|
6
6
|
"CLIENT_URL_ACCOUNT": "https://account.minderable.com",
|
|
7
|
+
"SERVER_URL_BFF": "https://bff.minderable.com",
|
|
7
8
|
"SERVER_URL_AUTH": "https://api.auth.minderable.com"
|
|
8
9
|
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
export const getSubmitMetadata = async ({ clientId, deviceId }) => {
|
|
2
|
+
const battery = navigator.getBattery ? await navigator.getBattery() : {}
|
|
3
|
+
const metadataObj = {
|
|
4
|
+
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
|
5
|
+
language: window.navigator.language,
|
|
6
|
+
pageTitle: document.title,
|
|
7
|
+
referrer: document.referrer,
|
|
8
|
+
pageURL: window.location.href,
|
|
9
|
+
platform: window.navigator.platform,
|
|
10
|
+
deviceMemory: navigator.deviceMemory,
|
|
11
|
+
screenWidth: window.screen.width,
|
|
12
|
+
screenHeight: window.screen.height,
|
|
13
|
+
colorDepth: window.screen.colorDepth,
|
|
14
|
+
maxScreenWidth: window.screen.width * window.devicePixelRatio,
|
|
15
|
+
maxScreenHeight: window.screen.height * window.devicePixelRatio,
|
|
16
|
+
pixelRatio: window.devicePixelRatio,
|
|
17
|
+
screenOrientation: screen?.orientation?.type,
|
|
18
|
+
hardwareConcurrency: navigator.hardwareConcurrency,
|
|
19
|
+
touchSupport: 'ontouchstart' in window || navigator.maxTouchPoints > 0,
|
|
20
|
+
colorGamut: window.matchMedia("(color-gamut: srgb)").matches
|
|
21
|
+
? "srgb"
|
|
22
|
+
: window.matchMedia("(color-gamut: p3)").matches
|
|
23
|
+
? "p3"
|
|
24
|
+
: window.matchMedia("(color-gamut: rec2020)").matches
|
|
25
|
+
? "rec2020"
|
|
26
|
+
: "unknown",
|
|
27
|
+
rtt: navigator?.connection?.rtt,
|
|
28
|
+
hasGamepad: navigator.getGamepads ? navigator.getGamepads().length > 0 : false,
|
|
29
|
+
keyboardEnabled: "onkeydown" in document,
|
|
30
|
+
cookieEnabled: navigator.cookieEnabled,
|
|
31
|
+
javaEnabled: navigator.javaEnabled ? navigator.javaEnabled() : false,
|
|
32
|
+
doNotTrack: navigator.doNotTrack,
|
|
33
|
+
networkType: navigator?.connection?.effectiveType,
|
|
34
|
+
downlinkSpeed: navigator?.connection?.downlink,
|
|
35
|
+
batteryLevel: battery?.level * 100,
|
|
36
|
+
batteryCharging: battery?.charging
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return {
|
|
40
|
+
submit_timestamp: new Date().toISOString(),
|
|
41
|
+
submit_device_id: deviceId,
|
|
42
|
+
submit_client_id: clientId,
|
|
43
|
+
submit_user_agent: window.navigator.userAgent,
|
|
44
|
+
submit_metadata: Object.entries(metadataObj).map(([key, value]) => `${key}:${value ? value : 'unknown'}`).join(" | ")
|
|
45
|
+
}
|
|
46
|
+
}
|
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "1.0.
|
|
2
|
+
"version": "1.0.29",
|
|
3
3
|
"name": "@fullstackunicorn/create-root",
|
|
4
4
|
"author": "lucanigido (https://fullstackunicorn.dev/author/lucanigido)",
|
|
5
5
|
"description": "create-root: bootstrapper that writes config assets and installs @fullstackunicorn/root",
|