session-sync-auth-site 0.2.0 → 0.3.1
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 +22 -3
- package/package.json +1 -1
- package/src/authenticate.js +19 -1
- package/src/createDBTables.js +1 -1
- package/src/sessionSyncAuthFrontend.js +51 -7
- package/src/setUpSessionSyncAuthRoutes.js +40 -6
- package/test/app.html +2 -0
package/README.md
CHANGED
|
@@ -65,9 +65,18 @@ app.use(authenticate({
|
|
|
65
65
|
```js
|
|
66
66
|
setUpSessionSyncAuthRoutes({
|
|
67
67
|
app, // required
|
|
68
|
-
siteId, // required
|
|
69
|
-
authDomain, // required
|
|
70
|
-
jwtSecret, // required
|
|
68
|
+
siteId, // required (unless getSetupInfo provided)
|
|
69
|
+
authDomain, // required (unless getSetupInfo provided)
|
|
70
|
+
jwtSecret, // required (unless getSetupInfo provided)
|
|
71
|
+
getSetupInfo: req => { // useful for multi-tenancy setups
|
|
72
|
+
// fetch the needed values based upon req
|
|
73
|
+
return {
|
|
74
|
+
siteId,
|
|
75
|
+
authDomain,
|
|
76
|
+
jwtSecret,
|
|
77
|
+
extraUserTableValues, // optional
|
|
78
|
+
}
|
|
79
|
+
},
|
|
71
80
|
protocol: 'https',
|
|
72
81
|
paths: {
|
|
73
82
|
getUser: '/get-user',
|
|
@@ -93,15 +102,25 @@ setUpSessionSyncAuthRoutes({
|
|
|
93
102
|
callbacks: {
|
|
94
103
|
canceledLogin: ({ origin }) => {},
|
|
95
104
|
successfulLogin: ({ origin, accessToken }) => {},
|
|
105
|
+
canceledAccountUpdate: ({ origin }) => {},
|
|
106
|
+
successfulAccountUpdate: ({ origin }) => {},
|
|
96
107
|
successfulLogout: ({ origin }) => {},
|
|
97
108
|
unnecessaryLogout: ({ origin }) => {},
|
|
98
109
|
error: ({ errorMessage }) => {},
|
|
99
110
|
},
|
|
111
|
+
// enabledSSR: true, // Include this if you use server-side-rendering
|
|
100
112
|
})
|
|
101
113
|
|
|
102
114
|
// To change the default origin...
|
|
103
115
|
// window.sessionSyncAuth.setDefaultOrigin('https://my-backend-domain.com')
|
|
104
116
|
|
|
117
|
+
// When getting data from your backend via AJAX, add in a x-access-token header...
|
|
118
|
+
// const response = await fetch(url, {
|
|
119
|
+
// headers: {
|
|
120
|
+
// 'x-access-token': window.sessionSyncAuth.getAccessToken(),
|
|
121
|
+
// },
|
|
122
|
+
// })
|
|
123
|
+
|
|
105
124
|
</script>
|
|
106
125
|
<head>
|
|
107
126
|
|
package/package.json
CHANGED
package/src/authenticate.js
CHANGED
|
@@ -26,7 +26,25 @@ const authenticate = ({
|
|
|
26
26
|
console.log('...DB connection established for session-sync-auth-site.')
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
let accessToken = req.headers['x-access-token']
|
|
30
|
+
|
|
31
|
+
if(!accessToken && req.query.accessToken) {
|
|
32
|
+
accessToken = req.query.accessToken
|
|
33
|
+
|
|
34
|
+
// set cookie in case they have enableSSR=true
|
|
35
|
+
let cookieObj = {}
|
|
36
|
+
try {
|
|
37
|
+
cookieObj = JSON.parse(req.cookies.sessionSyncAuthAccessTokenByOrigin)
|
|
38
|
+
} catch(e) {}
|
|
39
|
+
cookieObj[`${req.protocol}://${req.headers.host}`] = accessToken
|
|
40
|
+
res.cookie('sessionSyncAuthAccessTokenByOrigin', JSON.stringify(cookieObj))
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if(!accessToken) {
|
|
44
|
+
try {
|
|
45
|
+
accessToken = JSON.parse(req.cookies.sessionSyncAuthAccessTokenByOrigin)[`${req.protocol}://${req.headers.host}`]
|
|
46
|
+
} catch(err) {}
|
|
47
|
+
}
|
|
30
48
|
|
|
31
49
|
if(!accessToken) return next()
|
|
32
50
|
|
package/src/createDBTables.js
CHANGED
|
@@ -55,7 +55,7 @@ const setUpConnection = require('./setUpConnection')
|
|
|
55
55
|
console.log(``)
|
|
56
56
|
console.log(`Tables ${userTableName} and ${sessionTableName} created.`)
|
|
57
57
|
console.log(``)
|
|
58
|
-
console.log(`Note: The user table may be extended with other columns so long as they are nullable.`)
|
|
58
|
+
console.log(`Note: The user table may be extended with other columns so long as they are nullable or provided via extraUserTableValues.`)
|
|
59
59
|
console.log(``)
|
|
60
60
|
|
|
61
61
|
} catch(err) {
|
|
@@ -1,12 +1,36 @@
|
|
|
1
1
|
;(() => {
|
|
2
2
|
|
|
3
|
+
let enableSSR = false
|
|
3
4
|
let defaultOrigin
|
|
4
5
|
const setDefaultOrigin = origin => {
|
|
5
6
|
defaultOrigin = origin
|
|
6
7
|
}
|
|
7
8
|
|
|
9
|
+
const getLocalStorageOrCookieItem = item => {
|
|
10
|
+
if(enableSSR) {
|
|
11
|
+
const desiredCookieNameAndValue = (
|
|
12
|
+
document.cookie.split(/; ?/g)
|
|
13
|
+
.map(cookieNameAndValue => cookieNameAndValue.split(/=/g))
|
|
14
|
+
.find(([ cookieName ]) => cookieName === item)
|
|
15
|
+
)
|
|
16
|
+
if(!desiredCookieNameAndValue) return undefined
|
|
17
|
+
return decodeURIComponent(desiredCookieNameAndValue[1])
|
|
18
|
+
} else {
|
|
19
|
+
return localStorage.getItem(item)
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const setLocalStorageOrCookieItem = (item, value) => {
|
|
24
|
+
if(enableSSR) {
|
|
25
|
+
const expireStr = new Date(Date.now() + (365 * 1000 * 60 * 60 * 24)).toUTCString() // one year
|
|
26
|
+
document.cookie = item + "=" + encodeURIComponent(value) + ";expires=" + expireStr + ";path=/";
|
|
27
|
+
} else {
|
|
28
|
+
localStorage.setItem(item, value)
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
8
32
|
const getAccessToken = ({ origin=defaultOrigin }={}) => (
|
|
9
|
-
JSON.parse(
|
|
33
|
+
JSON.parse(getLocalStorageOrCookieItem('sessionSyncAuthAccessTokenByOrigin') || `{}`)[origin]
|
|
10
34
|
)
|
|
11
35
|
|
|
12
36
|
const getQueryStringAddOn = extraQueryParamsForCallbacks => {
|
|
@@ -19,15 +43,24 @@
|
|
|
19
43
|
return queryStringAddOn
|
|
20
44
|
}
|
|
21
45
|
|
|
22
|
-
const logIn = async ({ origin=defaultOrigin, extraQueryParamsForCallbacks }={}) => {
|
|
23
|
-
const cancelRedirectUrl = `${location.href.replace(/\?.*$/, '')}?action=canceledLogin&origin=${encodeURIComponent(origin)}${getQueryStringAddOn(extraQueryParamsForCallbacks)}`
|
|
24
|
-
const loggedInRedirectUrl = `${location.href.replace(/\?.*$/, '')}?action=successfulLogin&origin=${encodeURIComponent(origin)}&accessToken=ACCESS_TOKEN${getQueryStringAddOn(extraQueryParamsForCallbacks)}`
|
|
46
|
+
const logIn = async ({ origin=defaultOrigin, extraQueryParamsForCallbacks, redirectHref }={}) => {
|
|
47
|
+
const cancelRedirectUrl = `${redirectHref || location.href.replace(/\?.*$/, '')}?action=canceledLogin&origin=${encodeURIComponent(origin)}${getQueryStringAddOn(extraQueryParamsForCallbacks)}`
|
|
48
|
+
const loggedInRedirectUrl = `${redirectHref || location.href.replace(/\?.*$/, '')}?action=successfulLogin&origin=${encodeURIComponent(origin)}&accessToken=ACCESS_TOKEN${getQueryStringAddOn(extraQueryParamsForCallbacks)}`
|
|
25
49
|
|
|
26
50
|
const queryString = `cancelRedirectUrl=${encodeURIComponent(cancelRedirectUrl)}&loggedInRedirectUrl=${encodeURIComponent(loggedInRedirectUrl)}`
|
|
27
51
|
|
|
28
52
|
window.location = `${origin}/log-in?${queryString}`
|
|
29
53
|
}
|
|
30
54
|
|
|
55
|
+
const updateAccount = async ({ origin=defaultOrigin, extraQueryParamsForCallbacks, redirectHref }={}) => {
|
|
56
|
+
const cancelRedirectUrl = `${redirectHref || location.href.replace(/\?.*$/, '')}?action=canceledAccountUpdate&origin=${encodeURIComponent(origin)}${getQueryStringAddOn(extraQueryParamsForCallbacks)}`
|
|
57
|
+
const updatedRedirectUrl = `${redirectHref || location.href.replace(/\?.*$/, '')}?action=successfulAccountUpdate&origin=${encodeURIComponent(origin)}${getQueryStringAddOn(extraQueryParamsForCallbacks)}`
|
|
58
|
+
|
|
59
|
+
const queryString = `cancelRedirectUrl=${encodeURIComponent(cancelRedirectUrl)}&updatedRedirectUrl=${encodeURIComponent(updatedRedirectUrl)}`
|
|
60
|
+
|
|
61
|
+
window.location = `${origin}/update-account?${queryString}`
|
|
62
|
+
}
|
|
63
|
+
|
|
31
64
|
const getUser = async ({ origin=defaultOrigin }={}) => {
|
|
32
65
|
const response = await fetch(`${origin}/get-user`, {
|
|
33
66
|
headers: {
|
|
@@ -48,10 +81,14 @@
|
|
|
48
81
|
window.location = `${origin}/log-out?${queryString}`
|
|
49
82
|
}
|
|
50
83
|
|
|
51
|
-
const init = ({ defaultOrigin, callbacks }) => {
|
|
84
|
+
const init = ({ defaultOrigin, callbacks, enableSSR: newEnableSSRValue }) => {
|
|
52
85
|
|
|
53
86
|
if(defaultOrigin) setDefaultOrigin(defaultOrigin)
|
|
54
87
|
|
|
88
|
+
if(typeof newEnableSSRValue === 'boolean') {
|
|
89
|
+
enableSSR = newEnableSSRValue
|
|
90
|
+
}
|
|
91
|
+
|
|
55
92
|
const getParam = regex => decodeURIComponent((window.location.search.match(regex) || [])[1] || '') || null
|
|
56
93
|
|
|
57
94
|
const origin = getParam(/[?&]origin=([^&]*)/)
|
|
@@ -60,13 +97,19 @@
|
|
|
60
97
|
const errorMessage = getParam(/[?&]errorMessage=([^&]*)/)
|
|
61
98
|
|
|
62
99
|
if(origin) {
|
|
63
|
-
const sessionSyncAuthAccessTokenByOrigin = JSON.parse(
|
|
100
|
+
const sessionSyncAuthAccessTokenByOrigin = JSON.parse(getLocalStorageOrCookieItem('sessionSyncAuthAccessTokenByOrigin') || `{}`)
|
|
64
101
|
if(accessToken) {
|
|
65
102
|
sessionSyncAuthAccessTokenByOrigin[origin] = accessToken
|
|
66
103
|
} else if([ 'successfulLogout', 'unnecessaryLogout' ].includes(action)) {
|
|
67
104
|
delete sessionSyncAuthAccessTokenByOrigin[origin]
|
|
68
105
|
}
|
|
69
|
-
|
|
106
|
+
setLocalStorageOrCookieItem('sessionSyncAuthAccessTokenByOrigin', JSON.stringify(sessionSyncAuthAccessTokenByOrigin))
|
|
107
|
+
|
|
108
|
+
if(accessToken && enableSSR) {
|
|
109
|
+
// refresh without the origin query param
|
|
110
|
+
window.location = `${location.href.replace(/\?.*$/, '')}${window.location.search.replace(/&?(?:origin)=(?:[^&]*)/g, '').replace(/^\?$/, '')}`
|
|
111
|
+
return
|
|
112
|
+
}
|
|
70
113
|
}
|
|
71
114
|
|
|
72
115
|
try {
|
|
@@ -84,6 +127,7 @@
|
|
|
84
127
|
const sessionSyncAuth = {
|
|
85
128
|
getAccessToken,
|
|
86
129
|
logIn,
|
|
130
|
+
updateAccount,
|
|
87
131
|
getUser,
|
|
88
132
|
logOut,
|
|
89
133
|
init,
|
|
@@ -7,16 +7,20 @@ const setUpSessionSyncAuthRoutes = ({
|
|
|
7
7
|
siteId,
|
|
8
8
|
authDomain,
|
|
9
9
|
jwtSecret,
|
|
10
|
+
getSetupInfo, // for multi-tenancy setup; sent req and must return the siteId, authDomain, and jwtSecret
|
|
10
11
|
protocol='https',
|
|
11
12
|
paths: {
|
|
12
13
|
getUser='/get-user',
|
|
13
14
|
logIn='/log-in',
|
|
15
|
+
updateAccount='/update-account',
|
|
14
16
|
logOut='/log-out',
|
|
15
17
|
authSync='/auth-sync',
|
|
16
18
|
}={},
|
|
17
19
|
languageColType='639-3', // OPTIONS: '639-1', '639-3', 'IETF'
|
|
18
20
|
}) => {
|
|
19
21
|
|
|
22
|
+
getSetupInfo = getSetupInfo || (async () => ({ siteId, jwtSecret, authDomain }))
|
|
23
|
+
|
|
20
24
|
app.get(getUser, (req, res, next) => {
|
|
21
25
|
res.json({
|
|
22
26
|
status: req.user ? 'Logged in' : 'Not logged in',
|
|
@@ -24,8 +28,9 @@ const setUpSessionSyncAuthRoutes = ({
|
|
|
24
28
|
})
|
|
25
29
|
})
|
|
26
30
|
|
|
27
|
-
app.get(logIn, (req, res, next) => {
|
|
31
|
+
app.get(logIn, async (req, res, next) => {
|
|
28
32
|
const { loggedInRedirectUrl, cancelRedirectUrl } = req.query
|
|
33
|
+
const { siteId, jwtSecret, authDomain } = await getSetupInfo(req)
|
|
29
34
|
|
|
30
35
|
const jwtData = jwt.sign(
|
|
31
36
|
{
|
|
@@ -38,17 +43,38 @@ const setUpSessionSyncAuthRoutes = ({
|
|
|
38
43
|
res.redirect(`${protocol}://${authDomain}/login?siteId=${encodeURIComponent(siteId)}&jwtData=${encodeURIComponent(jwtData)}`)
|
|
39
44
|
})
|
|
40
45
|
|
|
41
|
-
app.get(
|
|
46
|
+
app.get(updateAccount, async (req, res, next) => {
|
|
47
|
+
const { updatedRedirectUrl, cancelRedirectUrl } = req.query
|
|
48
|
+
const { siteId, jwtSecret, authDomain } = await getSetupInfo(req)
|
|
49
|
+
|
|
50
|
+
const jwtData = jwt.sign(
|
|
51
|
+
{
|
|
52
|
+
cancelRedirectUrl,
|
|
53
|
+
updatedRedirectUrl,
|
|
54
|
+
},
|
|
55
|
+
jwtSecret,
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
res.redirect(`${protocol}://${authDomain}/account?siteId=${encodeURIComponent(siteId)}&jwtData=${encodeURIComponent(jwtData)}`)
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
app.get(logOut, async (req, res, next) => {
|
|
42
62
|
const { redirectUrl, noLoginRedirectUrl } = req.query
|
|
63
|
+
const { siteId, jwtSecret, authDomain } = await getSetupInfo(req)
|
|
43
64
|
|
|
44
65
|
if(!req.user) {
|
|
45
66
|
console.warn(`logout attemped with no login`)
|
|
46
67
|
return res.redirect(noLoginRedirectUrl || redirectUrl)
|
|
47
68
|
}
|
|
48
69
|
|
|
70
|
+
const {
|
|
71
|
+
userTableColNameMap,
|
|
72
|
+
} = req.sessionSyncAuthSiteOptions
|
|
73
|
+
|
|
74
|
+
mapKeys
|
|
49
75
|
const jwtData = jwt.sign(
|
|
50
76
|
{
|
|
51
|
-
userId: req.user.id,
|
|
77
|
+
userId: req.user[userTableColNameMap.id || `id`],
|
|
52
78
|
redirectUrl,
|
|
53
79
|
},
|
|
54
80
|
jwtSecret,
|
|
@@ -59,20 +85,21 @@ const setUpSessionSyncAuthRoutes = ({
|
|
|
59
85
|
|
|
60
86
|
app.post(authSync, async (req, res, next) => {
|
|
61
87
|
const { payload } = req.body
|
|
88
|
+
const { jwtSecret, extraUserTableValues } = await getSetupInfo(req)
|
|
62
89
|
|
|
63
90
|
try {
|
|
64
91
|
|
|
65
92
|
if(!global.sessionSyncAuthSiteConnection) {
|
|
66
93
|
throw new Error('You must include the authenticate() middleware prior to calling setUpSessionSyncAuthRoutes.')
|
|
67
94
|
}
|
|
68
|
-
|
|
95
|
+
|
|
69
96
|
const {
|
|
70
97
|
userTableName,
|
|
71
98
|
sessionTableName,
|
|
72
99
|
userTableColNameMap,
|
|
73
100
|
sessionTableColNameMap,
|
|
74
101
|
} = req.sessionSyncAuthSiteOptions
|
|
75
|
-
|
|
102
|
+
|
|
76
103
|
const { users } = jwt.verify(payload, jwtSecret)
|
|
77
104
|
|
|
78
105
|
if(!users) throw "Invalid payload. Missing `payload.user`."
|
|
@@ -126,7 +153,7 @@ const setUpSessionSyncAuthRoutes = ({
|
|
|
126
153
|
|
|
127
154
|
variables[`user__${id}`] = getSnakeCaseOfKeys(
|
|
128
155
|
user,
|
|
129
|
-
[ 'id', 'email', 'name', 'image', 'language', 'created_at', 'updated_at' ],
|
|
156
|
+
[ 'id', 'email', 'name', 'image', 'language', 'gender', 'created_at', 'updated_at' ],
|
|
130
157
|
)
|
|
131
158
|
|
|
132
159
|
if(languageColType !== 'IETF') {
|
|
@@ -137,6 +164,13 @@ const setUpSessionSyncAuthRoutes = ({
|
|
|
137
164
|
variables[`user__${id}`].language = iso6393To1[variables[`user__${id}`].language] || 'en'
|
|
138
165
|
}
|
|
139
166
|
|
|
167
|
+
if(extraUserTableValues) {
|
|
168
|
+
variables[`user__${id}`] = {
|
|
169
|
+
...extraUserTableValues,
|
|
170
|
+
...variables[`user__${id}`],
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
140
174
|
mapKeys(variables[`user__${id}`], 'user')
|
|
141
175
|
|
|
142
176
|
queries.push(`
|
package/test/app.html
CHANGED
|
@@ -12,6 +12,8 @@
|
|
|
12
12
|
callbacks: {
|
|
13
13
|
canceledLogin: ({ origin }) => alert(`Canceled login to ${origin}`),
|
|
14
14
|
successfulLogin: ({ origin }) => alert(`Successful login to ${origin}`),
|
|
15
|
+
canceledAccountUpdate: ({ origin }) => alert(`Canceled login to ${origin}`),
|
|
16
|
+
successfulAccountUpdate: ({ origin }) => alert(`Successful login to ${origin}`),
|
|
15
17
|
successfulLogout: ({ origin }) => alert(`Successful logout from ${origin}`),
|
|
16
18
|
unnecessaryLogout: ({ origin }) => alert(`Unnecessary logout from ${origin}`),
|
|
17
19
|
error: ({ errorMessage }) => alert(`ERROR: ${errorMessage}`),
|