...
 
Commits (15)
...@@ -31,6 +31,7 @@ under `media/create` (playback possible using forked ...@@ -31,6 +31,7 @@ under `media/create` (playback possible using forked
- Mobile compatiblity for MoSys Editor - Mobile compatiblity for MoSys Editor
- The file `statics/env.js` can be overwritten to configure - The file `statics/env.js` can be overwritten to configure
the frontend at runtime the frontend at runtime
- Authentication service attempts silent login when token expires
### Changed ### Changed
...@@ -54,6 +55,8 @@ for YouTube sources to be GDPR compliant ...@@ -54,6 +55,8 @@ for YouTube sources to be GDPR compliant
to version 1.2.4 to version 1.2.4
- [mbjs-quasar](https://gitlab.rlp.net/motionbank/mbjs/quasar) - [mbjs-quasar](https://gitlab.rlp.net/motionbank/mbjs/quasar)
to version 2.2.0 to version 2.2.0
- [mbjs-api-client](https://gitlab.rlp.net/motionbank/mbjs/api-client)
to version 2.1.3
### Fixed ### Fixed
......
This diff is collapsed.
...@@ -62,11 +62,11 @@ module.exports = function (ctx) { ...@@ -62,11 +62,11 @@ module.exports = function (ctx) {
// //
// Hosts // Hosts
// //
API_HOST: JSON.stringify(process.env.API_HOST || 'https://api.motionbank.org'), API_HOST: JSON.stringify(process.env.API_HOST),
SOCKETS_HOST: JSON.stringify(process.env.SOCKETS_HOST || 'https://sockets.motionbank.org'), SOCKETS_HOST: JSON.stringify(process.env.SOCKETS_HOST),
STORAGE_HOST: JSON.stringify(process.env.STORAGE_HOST || 'https://storage.motionbank.org'), STORAGE_HOST: JSON.stringify(process.env.STORAGE_HOST),
PACKAGER_HOST: JSON.stringify(process.env.PACKAGER_HOST || 'https://packager.motionbank.org'), PACKAGER_HOST: JSON.stringify(process.env.PACKAGER_HOST),
TRANSCODER_HOST: JSON.stringify(process.env.TRANSCODER_HOST || 'https://transcoder.motionbank.org'), TRANSCODER_HOST: JSON.stringify(process.env.TRANSCODER_HOST),
// //
// Resources // Resources
// //
...@@ -76,16 +76,16 @@ module.exports = function (ctx) { ...@@ -76,16 +76,16 @@ module.exports = function (ctx) {
// //
// Auth0 // Auth0
// //
AUTH0_DOMAIN: JSON.stringify(process.env.AUTH0_DOMAIN || 'auth.motionbank.org'), AUTH0_DOMAIN: JSON.stringify(process.env.AUTH0_DOMAIN),
AUTH0_CLIENT_ID: JSON.stringify(process.env.AUTH0_CLIENT_ID || '80t5TRU9MVhGDVnZ522CvX4hutBxDB6U'), AUTH0_CLIENT_ID: JSON.stringify(process.env.AUTH0_CLIENT_ID),
AUTH0_REDIRECT_URL: JSON.stringify(process.env.AUTH0_REDIRECT_URL || null), AUTH0_REDIRECT_URL: JSON.stringify(process.env.AUTH0_REDIRECT_URL),
AUTH0_AUDIENCE: JSON.stringify(process.env.AUTH0_AUDIENCE || 'https://api.motionbank.org'), AUTH0_AUDIENCE: JSON.stringify(process.env.AUTH0_AUDIENCE),
// //
// API Keys // API Keys
// //
SENTRY_DSN: JSON.stringify(process.env.SENTRY_DSN || null), SENTRY_DSN: JSON.stringify(process.env.SENTRY_DSN),
YOUTUBE_API_KEY: JSON.stringify(process.env.YOUTUBE_API_KEY || null), YOUTUBE_API_KEY: JSON.stringify(process.env.YOUTUBE_API_KEYl),
VIMEO_ACCESS_TOKEN: JSON.stringify(process.env.VIMEO_ACCESS_TOKEN || null), VIMEO_ACCESS_TOKEN: JSON.stringify(process.env.VIMEO_ACCESS_TOKEN),
// //
// App config // App config
// //
......
...@@ -13,11 +13,11 @@ const ...@@ -13,11 +13,11 @@ const
keytarAccount = os.userInfo().username keytarAccount = os.userInfo().username
function getAuthenticationURL () { function getAuthenticationURL () {
return 'https://' + (window.AUTH0_DOMAIN || process.env.AUTH0_DOMAIN) + '/authorize?' + return 'https://' + (process.env.AUTH0_DOMAIN) + '/authorize?' +
'audience=' + (window.AUTH0_AUDIENCE || process.env.AUTH0_AUDIENCE) + '&' + 'audience=' + (process.env.AUTH0_AUDIENCE) + '&' +
'scope=openid profile offline_access&' + 'scope=openid profile offline_access&' +
'response_type=code&' + 'response_type=code&' +
'client_id=' + (window.AUTH0_CLIENT_ID || process.env.AUTH0_CLIENT_ID) + '&' + 'client_id=' + (process.env.AUTH0_CLIENT_ID) + '&' +
'redirect_uri=' + redirectUri 'redirect_uri=' + redirectUri
} }
...@@ -25,9 +25,9 @@ async function refreshTokens () { ...@@ -25,9 +25,9 @@ async function refreshTokens () {
const refreshToken = await keytar.getPassword(keytarService, keytarAccount) const refreshToken = await keytar.getPassword(keytarService, keytarAccount)
if (!refreshToken) return throw new Error('No refresh token found') if (!refreshToken) return throw new Error('No refresh token found')
const result = await axios.post(`https://${window.AUTH0_DOMAIN || process.env.AUTH0_DOMAIN}/oauth/token`, { const result = await axios.post(`https://${process.env.AUTH0_DOMAIN}/oauth/token`, {
grant_type: 'refresh_token', grant_type: 'refresh_token',
client_id: window.AUTH0_CLIENT_ID || process.env.AUTH0_CLIENT_ID, client_id: process.env.AUTH0_CLIENT_ID,
refresh_token: refreshToken refresh_token: refreshToken
}, { }, {
headers: {'Content-Type': 'application/json'} headers: {'Content-Type': 'application/json'}
...@@ -43,12 +43,12 @@ async function loadTokens (callbackURL) { ...@@ -43,12 +43,12 @@ async function loadTokens (callbackURL) {
const exchangeOptions = { const exchangeOptions = {
'grant_type': 'authorization_code', 'grant_type': 'authorization_code',
'client_id': window.AUTH0_CLIENT_ID || process.env.AUTH0_CLIENT_ID, 'client_id': process.env.AUTH0_CLIENT_ID,
'code': query.code, 'code': query.code,
'redirect_uri': redirectUri 'redirect_uri': redirectUri
} }
const result = await axios.post(`https://${window.AUTH0_DOMAIN || process.env.AUTH0_DOMAIN}/oauth/token`, exchangeOptions, { const result = await axios.post(`https://${process.env.AUTH0_DOMAIN}/oauth/token`, exchangeOptions, {
headers: { headers: {
'Content-Type': 'application/json' 'Content-Type': 'application/json'
} }
...@@ -66,7 +66,7 @@ async function logout () { ...@@ -66,7 +66,7 @@ async function logout () {
} }
function getLogOutUrl () { function getLogOutUrl () {
return `https://${window.AUTH0_DOMAIN || process.env.AUTH0_DOMAIN}/v2/logout` return `https://${process.env.AUTH0_DOMAIN}/v2/logout`
} }
export { export {
......
...@@ -3,14 +3,15 @@ import WebAuth from 'mbjs-api-client/src/web' ...@@ -3,14 +3,15 @@ import WebAuth from 'mbjs-api-client/src/web'
export default ({ Vue }) => { export default ({ Vue }) => {
const apiClient = new WebAuth({ const apiClient = new WebAuth({
auth: { auth: {
domain: process.env.AUTH0_DOMAIN, domain: process.env.AUTH0_DOMAIN || window.AUTH0_DOMAIN,
clientID: process.env.AUTH0_CLIENT_ID, clientID: process.env.AUTH0_CLIENT_ID || window.AUTH0_CLIENT_ID,
redirectUri: process.env.AUTH0_REDIRECT_URL || `${document.location.origin}/users/callback`, redirectUri: process.env.AUTH0_REDIRECT_URL || `${document.location.origin}/users/callback`,
audience: process.env.AUTH0_AUDIENCE, audience: process.env.AUTH0_AUDIENCE || window.AUTH0_AUDIENCE,
scope: 'openid profile read write', scope: 'openid profile read write',
responseType: 'token id_token' responseType: 'token id_token',
prompt: 'none'
}, },
host: process.env.API_HOST host: process.env.API_HOST || window.API_HOST
}) })
Vue.prototype.$api = apiClient Vue.prototype.$api = apiClient
} }
import AuthService from 'mbjs-quasar/src/lib/auth-service' import AuthServiceWeb from 'mbjs-api-client/src/auth-service-web'
import AuthServiceElectron from 'mbjs-quasar/src/lib/auth-service-electron' import AuthServiceElectron from 'mbjs-quasar/src/lib/auth-service-electron'
export default ({ Vue }) => { export default ({ Vue }) => {
...@@ -7,13 +7,14 @@ export default ({ Vue }) => { ...@@ -7,13 +7,14 @@ export default ({ Vue }) => {
authService = new AuthServiceElectron() authService = new AuthServiceElectron()
} }
else { else {
authService = new AuthService({ authService = new AuthServiceWeb({
domain: window.AUTH0_DOMAIN || process.env.AUTH0_DOMAIN, domain: process.env.AUTH0_DOMAIN || window.AUTH0_DOMAIN,
clientID: window.AUTH0_CLIENT_ID || process.env.AUTH0_CLIENT_ID, clientID: process.env.AUTH0_CLIENT_ID || window.AUTH0_CLIENT_ID,
redirectUri: `${document.location.origin}/users/callback`, redirectUri: process.env.AUTH0_REDIRECT_URL || `${document.location.origin}/users/callback`,
audience: window.AUTH0_AUDIENCE || process.env.AUTH0_AUDIENCE, audience: process.env.AUTH0_AUDIENCE || window.AUTH0_AUDIENCE,
scope: 'openid profile read write', scope: 'openid profile read write',
responseType: 'token id_token' responseType: 'token id_token',
prompt: 'none'
}) })
} }
......
...@@ -28,47 +28,38 @@ Router.beforeEach((to, from, next) => { ...@@ -28,47 +28,38 @@ Router.beforeEach((to, from, next) => {
else cb() else cb()
} }
waitForStore(Router.app, () => { waitForStore(Router.app, () => {
if (!Router.app.$store.state.auth.user) { Router.app.$auth.checkSession(Router.app.$store).catch(() => {
Router.app.$auth.checkSession(Router.app.$store).catch(() => { if (to.meta.private) {
if (to.meta.private) { Router.app.$store.commit('auth/setRedirect', to.fullPath)
Router.app.$store.commit('auth/setRedirect', to.fullPath) Router.app.$auth.authenticate()
Router.app.$auth.authenticate() }
}).then(result => {
if (result) {
const { user, first } = result
if (first) {
console.debug('Auth0 first login', user)
next({ name: 'users.manage', params: { isFirst: true, redirect: to } })
} }
}).then(result => { else {
if (result) { if (to.meta.feature) {
const { user, first } = result if (userHasFeature(Router.app.$store.state.auth.user, to.meta.feature)) next()
if (first) { else next({ name: 'site.welcome' })
console.debug('Auth0 first login', user)
next({ name: 'users.manage', params: { isFirst: true, redirect: to } })
}
else {
if (to.meta.feature) {
if (userHasFeature(Router.app.$store.state.auth.user, to.meta.feature)) next()
else next({ name: 'site.welcome' })
}
next()
} }
next()
} }
else if (to.meta.private) { }
if (process.env.IS_ELECTRON) next() else if (to.meta.private) {
else { if (process.env.IS_ELECTRON) next()
Router.app.$store.commit('auth/setRedirect', to.fullPath) else {
Router.app.$auth.authenticate() Router.app.$store.commit('auth/setRedirect', to.fullPath)
} Router.app.$auth.authenticate()
} }
else next()
}).catch(err => {
Router.app.$captureException(err)
Router.app.$auth.logout()
})
}
else {
if (to.meta.feature) {
if (userHasFeature(Router.app.$store.state.auth.user, to.meta.feature)) next()
else next({ name: 'site.welcome' })
} }
next() else next()
} }).catch(err => {
Router.app.$captureException(err)
Router.app.$auth.logout()
})
}) })
}) })
......
...@@ -48,14 +48,15 @@ else { ...@@ -48,14 +48,15 @@ else {
/** Instantiate Motion Bank API Client */ /** Instantiate Motion Bank API Client */
apiClient = new WebAuth({ apiClient = new WebAuth({
auth: { auth: {
domain: process.env.AUTH0_DOMAIN, domain: process.env.AUTH0_DOMAIN || window.AUTH0_DOMAIN,
clientID: process.env.AUTH0_CLIENT_ID, clientID: process.env.AUTH0_CLIENT_ID || window.AUTH0_CLIENT_ID,
redirectUri: process.env.AUTH0_REDIRECT_URL || `${document.location.origin}/users/callback`, redirectUri: process.env.AUTH0_REDIRECT_URL || `${document.location.origin}/users/callback`,
audience: process.env.AUTH0_AUDIENCE, audience: process.env.AUTH0_AUDIENCE || window.AUTH0_AUDIENCE,
scope: 'openid profile read write', scope: 'openid profile read write',
responseType: 'token id_token' responseType: 'token id_token',
prompt: 'none'
}, },
host: process.env.API_HOST host: process.env.API_HOST || window.API_HOST
}) })
} }
......
...@@ -45,19 +45,9 @@ const metadata = { ...@@ -45,19 +45,9 @@ const metadata = {
} }
} }
if (payload.id) { if (payload.id) {
const titleQuery = { metadata = await context.dispatch('fetchTitle', [metadata, payload])
'target.id': typeof payload === 'string' ? `${BASE_URI}/annotations/${payload}` : payload.id,
'body.purpose': 'describing',
'body.type': 'TextualBody'
}
const titleResult = await context.dispatch('annotations/find', titleQuery, {root: true})
if (titleResult && titleResult.items && titleResult.items.length) {
metadata.titleAnnotation = titleResult.items[0]
if (metadata.title) metadata.originalTitle = metadata.title
metadata.title = titleResult.items[0].body.value
}
} }
console.debug('metadata', metadata) console.debug('metadata/get', metadata)
return metadata return metadata
}, },
async getLocal (context, payload) { async getLocal (context, payload) {
...@@ -67,8 +57,12 @@ const metadata = { ...@@ -67,8 +57,12 @@ const metadata = {
if (typeof payload === 'string') { if (typeof payload === 'string') {
payload = await context.dispatch('annotations/get', payload, { root: true }) payload = await context.dispatch('annotations/get', payload, { root: true })
} }
let metadata let metadata = context.state.cache[payload.body.source.id] || {}
metadata = context.state.cache[payload.body.source.id] || {} metadata = await context.dispatch('fetchTitle', [metadata, payload])
console.debug('metadata/getLocal', metadata)
return metadata
},
async fetchTitle (context, [metadata, payload]) {
const titleQuery = { const titleQuery = {
'target.id': typeof payload === 'string' ? `${BASE_URI}/annotations/${payload}` : payload.id, 'target.id': typeof payload === 'string' ? `${BASE_URI}/annotations/${payload}` : payload.id,
'body.purpose': 'describing', 'body.purpose': 'describing',
...@@ -80,7 +74,7 @@ const metadata = { ...@@ -80,7 +74,7 @@ const metadata = {
if (metadata.title) metadata.originalTitle = metadata.title if (metadata.title) metadata.originalTitle = metadata.title
metadata.title = titleResult.items[0].body.value metadata.title = titleResult.items[0].body.value
} }
console.debug('metadata', metadata) console.debug('metadata/fetchTitle', metadata)
return metadata return metadata
} }
} }
......
...@@ -24,9 +24,17 @@ const vocabularies = { ...@@ -24,9 +24,17 @@ const vocabularies = {
const headers = { const headers = {
Authorization: `Bearer ${localStorage.getItem('access_token')}` Authorization: `Bearer ${localStorage.getItem('access_token')}`
} }
const result = await axios.get(`${process.env.API_HOST}/pba/pieces`, { headers }) let pieces = []
let pieces = result.data.sort((a, b) => a.label.replace(/\W/g, '').localeCompare(b.label.replace(/\W/g, ''))) try {
if (limit) pieces = pieces.splice(0, limit) const result = await axios.get(`${process.env.API_HOST}/pba/pieces`, {headers})
if (Array.isArray(result.data)) {
pieces = result.data.sort((a, b) => a.label.replace(/\W/g, '').localeCompare(b.label.replace(/\W/g, '')))
if (limit) pieces = pieces.splice(0, limit)
}
}
catch (err) {
console.error('Failed to load PBA vocabularies:', err.message || err.code)
}
for (let piece of pieces) { for (let piece of pieces) {
const result = await axios.get(`${process.env.API_HOST}/pba/pieces/${piece.piece_id}/titles`, {headers}) const result = await axios.get(`${process.env.API_HOST}/pba/pieces/${piece.piece_id}/titles`, {headers})
context.commit('addTermsForScope', [piece.piece_id, result.data.map(title => { context.commit('addTermsForScope', [piece.piece_id, result.data.map(title => {
......