1 | const request = require('superagent')
|
2 |
|
3 | export default class Authentication {
|
4 | constructor (opts = {}) {
|
5 | let { authUrl, clientId, storage, loginUrl, logoutUrl, userFetchUrl, accessCheckUrl } = opts
|
6 |
|
7 | this.authUrl = authUrl || 'https://moauth.fagbokforlaget.no'
|
8 | this.currentUser = undefined
|
9 | this.token = undefined
|
10 | this.clientId = clientId
|
11 | this.storage = storage || window.localStorage
|
12 | this.loginUrl = loginUrl || this.authUrl + '/_auth/login'
|
13 | this.logoutUrl = logoutUrl
|
14 | this.userFetchUrl = userFetchUrl || this.authUrl + '/_auth/user?token='
|
15 | this.accessCheckUrl = accessCheckUrl || this.authUrl + '/_auth/access'
|
16 | }
|
17 |
|
18 | _loginUrl (redirectUrl, scope = undefined) {
|
19 | if (!this.loginUrl.includes('?')) {
|
20 | this.loginUrl += '?'
|
21 | }
|
22 | return this.loginUrl + '&client_id=' + (this.clientId || 'generic') + '&redirect_url=' + encodeURIComponent(redirectUrl) + '&scope=' + (scope || 'dbok')
|
23 | }
|
24 |
|
25 | _parseQueryString (loc) {
|
26 | const pl = /\+/g
|
27 | const search = /([^&=]+)=?([^&]*)/g
|
28 | const decode = function (s) { return decodeURIComponent(s.replace(pl, ' ')) }
|
29 | let urlParams = {}
|
30 | let query = loc.substring(1)
|
31 |
|
32 | if (/\?/.test(query)) {
|
33 | query = query.split('?')[1]
|
34 | }
|
35 |
|
36 | while (1) {
|
37 | const match = search.exec(query)
|
38 |
|
39 | if (!match) {
|
40 | break
|
41 | }
|
42 | urlParams[decode(match[1])] = decode(match[2])
|
43 | }
|
44 |
|
45 | return urlParams
|
46 | }
|
47 |
|
48 | authorize (obj = {}) {
|
49 | let { redirectUrl, scope } = obj
|
50 |
|
51 | window.location = this._loginUrl(redirectUrl || window.location, scope)
|
52 | }
|
53 |
|
54 | getUser () {
|
55 | let storeUser = this.storage.getItem('user')
|
56 |
|
57 | if (this.currentUser) {
|
58 | return this.currentUser
|
59 | }
|
60 | if (storeUser) {
|
61 | return JSON.parse(storeUser)
|
62 | }
|
63 | return undefined
|
64 | }
|
65 |
|
66 | checkToken (loc = window.location.search) {
|
67 | let params = this._parseQueryString(loc)
|
68 | let self = this
|
69 |
|
70 | self.token = params.token || params.access_token || this.storage.getItem('token') || undefined
|
71 |
|
72 | return new Promise((resolve, reject) => {
|
73 | if (self.isAuthenticated() && self.token && typeof self.token !== 'undefined') {
|
74 | resolve(self.getUser())
|
75 | }
|
76 |
|
77 | if (self.token && typeof self.token !== 'undefined') {
|
78 | if (window) {
|
79 | window.history.replaceState({}, '', window.location.pathname + window.location.hash || '')
|
80 | }
|
81 | self.storage.setItem('token', self.token)
|
82 | self.fetchUser(this.userFetchUrl + encodeURIComponent(self.token))
|
83 | .then((user) => {
|
84 | resolve(user)
|
85 | })
|
86 | .catch((err) => {
|
87 | reject(err)
|
88 | })
|
89 | } else {
|
90 | reject(new Error('access token not found'))
|
91 | }
|
92 | })
|
93 | }
|
94 |
|
95 | checkAccess (productIds = []) {
|
96 | const token = this.token || this.storage.getItem('token') || undefined
|
97 | const products = Array.isArray(productIds) ? productIds : [productIds]
|
98 | const user = this.getUser()
|
99 |
|
100 | return new Promise(async (resolve, reject) => {
|
101 | if (token && typeof token !== 'undefined') {
|
102 | const allowedProducts = await this.fetchAccess(this.accessCheckUrl, { token: token, productIds: products })
|
103 | resolve({ success: true, user: user, products: allowedProducts })
|
104 | } else {
|
105 | throw new Error('access token not found')
|
106 | }
|
107 | })
|
108 | }
|
109 |
|
110 | fetchUser (url) {
|
111 | let self = this
|
112 |
|
113 | return new Promise((resolve, reject) => {
|
114 | request
|
115 | .get(url)
|
116 | .then(response => {
|
117 | if (response.statusCode === 200 && response.body) {
|
118 | let resp = response.body
|
119 | let user = resp.user || resp.objects[0]
|
120 |
|
121 | self.storage.setItem('user', JSON.stringify(user))
|
122 | resolve(user)
|
123 | } else {
|
124 | reject(new Error('authentication failed: Invalid response'))
|
125 | }
|
126 | })
|
127 | .catch(err => {
|
128 | reject(err)
|
129 | })
|
130 | })
|
131 | }
|
132 |
|
133 | fetchAccess (url, body) {
|
134 | return new Promise((resolve, reject) => {
|
135 | request
|
136 | .post(url)
|
137 | .send(body)
|
138 | .set('Accept', 'application/json')
|
139 | .then(response => {
|
140 | if (response.statusCode === 200 && response.body) {
|
141 | if (response.body.success) {
|
142 | resolve(response.body.products)
|
143 | } else {
|
144 | reject(new Error('This user does not have access to this product'))
|
145 | }
|
146 | }
|
147 | })
|
148 | .catch(error => {
|
149 | reject(error)
|
150 | })
|
151 | })
|
152 | }
|
153 |
|
154 | async logout (url) {
|
155 | this.storage.removeItem('user')
|
156 | this.storage.removeItem('token')
|
157 |
|
158 | url = url || this.logoutUrl
|
159 |
|
160 | if (url) window.location = url
|
161 | }
|
162 |
|
163 | isAuthenticated () {
|
164 | let user = this.getUser()
|
165 |
|
166 | if (user) {
|
167 | return true
|
168 | }
|
169 | return false
|
170 | }
|
171 | }
|