feat(server/frontend): auth revamp WIP

This commit is contained in:
Dimitrie Stefanescu
2020-12-28 00:36:13 +02:00
parent 857dc5198e
commit 5947bd2c93
15 changed files with 285 additions and 271 deletions
+5 -5
View File
@@ -42,7 +42,6 @@ exports.init = async ( ) => {
}
if ( process.env.COMPRESSION ) {
debug( `speckle:startup` )( 'Using app level compression. Consider enabling this at a proxy level.' )
app.use( compression( ) )
}
@@ -77,7 +76,9 @@ exports.init = async ( ) => {
plugins: [
require( `${appRoot}/logging/apolloPlugin` )
],
tracing: process.env.NODE_ENV === 'development'
tracing: process.env.NODE_ENV === 'development',
introspection: true,
playground: true
} )
graphqlServer.applyMiddleware( { app: app } )
@@ -106,8 +107,7 @@ exports.startHttp = async ( app ) => {
debug( 'speckle:http-startup' )( '✨ Proxying frontend (dev mode):' )
debug( 'speckle:http-startup' )( `👉 main application: http://localhost:${port}/` )
debug( 'speckle:http-startup' )( `👉 auth application: http://localhost:${port}/auth` )
debug( 'speckle:hint' )( ` ️ Don't forget to run "npm run dev:frontend" in a different terminal to start the vue application.` )
debug( 'speckle:hint' )( '️ Don\'t forget to run "npm run dev:frontend" in a different terminal to start the vue application.' )
}
// Production mode -> serve things statically.
@@ -136,7 +136,7 @@ exports.startHttp = async ( app ) => {
app.use( Sentry.Handlers.errorHandler( ) )
server.on( 'listening', ( ) => {
debug( `speckle:startup` )( ` 🚀 My name is Spockle Server, and I'm running at ${server.address().port}` )
debug( 'speckle:startup' )( ` 🚀 My name is Spockle Server, and I'm running at ${server.address().port}` )
} )
server.listen( port )
-9
View File
@@ -13,7 +13,6 @@ export async function checkAccessCodeAndGetTokens() {
let response = await getTokenFromAccessCode(accessCode)
// eslint-disable-next-line no-prototype-builtins
if (response.hasOwnProperty('token')) {
localStorage.clear()
localStorage.setItem('AuthToken', response.token)
localStorage.setItem('RefreshToken', response.refreshToken)
window.history.replaceState({}, document.title, '/')
@@ -68,14 +67,6 @@ export async function getTokenFromAccessCode(accessCode) {
return data
}
export function redirectToAuth() {
// Reaching this stage means we're initialising a full new auth flow,
// TIP: also means we need to refresh the app challenge as well.
localStorage.setItem('appChallenge', crs({ length: 10 }))
// Finally, redirect to the auth lock.
window.location = `/auth?appId=${appId}&challenge=${localStorage.getItem('appChallenge')}`
}
/**
* Signs out the current session
* @return {null}
+13 -28
View File
@@ -161,38 +161,23 @@ const router = new VueRouter({
router.beforeEach((to, from, next) => {
let uuid = localStorage.getItem('uuid')
let redirect = localStorage.getItem('shouldRedirectTo')
let path = to.path
let name = to.name
console.log(uuid, redirect, path, name)
if (!uuid && to.name !== 'Login' && to.name !== 'Register') {
localStorage.setItem('shouldRedirectTo', to.path)
if (!uuid && (to.name !== 'Login' && to.name !== 'Register')) return next({ name: 'Login' })
return next({ name: 'Login' })
}
if ((to.name === 'Login' || to.name === 'Register') && uuid) {
return next({ name: 'home' })
}
if (uuid && redirect && redirect !== to.path) {
localStorage.removeItem('shouldRedirectTo')
return next({ path: redirect })
}
return next()
// if (!(to.name === 'Login' || to.name === 'Register') && !localStorage.getItem('uuid')) {
// localStorage.setItem('shouldRedirectTo', to.path)
// return next({ name: 'Login' })
// } else if (
// (to.name === 'Login' || to.name === 'Register') &&
// !!localStorage.getItem('uuid') &&
// !!localStorage.getItem('shouldRedirectTo')
// ) {
// return next({ name: 'home' })
// } else if (localStorage.getItem('shouldRedirectTo')) {
// let path = localStorage.getItem('shouldRedirectTo')
// localStorage.removeItem('shouldRedirectTo')
// return next({ path })
// } else {
// return next()
// }
// else if (localStorage.getItem('uuid')) {
// let shouldRedirectTo = localStorage.getItem('shouldRedirectTo')
// localStorage.removeItem('shouldRedirectTo')
// if (shouldRedirectTo) {
// return next() // TODO: redirect to prev url
// } else return next()
// }
})
//TODO: include stream name in page title eg `My Cool Stream | Speckle`
+114 -114
View File
@@ -25,127 +25,127 @@
<script crossorigin src="https://unpkg.com/graphiql/graphiql.min.js"></script>
<script>
const graphQLFetcher = graphQLParams =>
fetch( '/graphql', {
method: 'post',
headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + localStorage.getItem( 'AuthToken' ) },
body: JSON.stringify( graphQLParams ),
} )
fetch( '/graphql', {
method: 'post',
headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + localStorage.getItem( 'AuthToken' ) },
body: JSON.stringify( graphQLParams ),
} )
.then( response => response.json( ) )
.catch( ( ) => response.text( ) )
function redirectToAuth( ) {
localStorage.setItem( 'challenge', Math.random( ).toString( 36 ).substring( 2, 15 ) + Math.random( ).toString( 36 ).substring( 2, 15 ) )
window.location = '/auth?appId=explorer&challenge=' + localStorage.getItem( 'challenge' )
}
function redirectToAuth( ) {
localStorage.setItem( 'challenge', Math.random( ).toString( 36 ).substring( 2, 15 ) + Math.random( ).toString( 36 ).substring( 2, 15 ) )
window.location = `/authn/verify/explorer/${localStorage.getItem( 'challenge' )}`
}
async function accessCodeExchange( accessCode ) {
window.history.replaceState( {}, document.title, '/explorer' )
async function accessCodeExchange( accessCode ) {
window.history.replaceState( {}, document.title, '/explorer' )
let response = await fetch( '/auth/token', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify( {
accessCode: accessCode,
appId: 'explorer',
appSecret: 'explorer',
challenge: localStorage.getItem( 'challenge' )
} )
let response = await fetch( '/auth/token', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify( {
accessCode: accessCode,
appId: 'explorer',
appSecret: 'explorer',
challenge: localStorage.getItem( 'challenge' )
} )
let data = await response.json( )
if ( data.hasOwnProperty( 'token' ) ) {
localStorage.removeItem( 'challenge' )
localStorage.setItem( 'AuthToken', data.token )
localStorage.setItem( 'RefreshToken', data.refreshToken )
await setUserName( )
await initGQL( data.token )
}
}
async function initGQL( token ) {
// GraphQLPlayground.init( document.getElementById( 'root' ), {
// endpointUrl: '/graphql',
// headers: {
// 'Authorization': 'Bearer ' + token
// }
// } )
ReactDOM.render(
React.createElement( GraphiQL, { fetcher: graphQLFetcher } ),
document.getElementById( 'graphiql' ),
)
await setServerInfo( )
}
async function setUserName( ) {
let testResponse = await fetch( '/graphql', {
method: 'POST',
headers: {
'Authorization': 'Bearer ' + localStorage.getItem( 'AuthToken' ),
'Content-Type': 'application/json'
},
body: JSON.stringify( { query: `{ user { name } }` } )
} )
let data = ( await testResponse.json( ) ).data
document.getElementById( 'username' ).innerHTML = data.user.name
}
async function setServerInfo( ) {
let testResponse = await fetch( '/graphql', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify( { query: `{ serverInfo { name company } }` } )
} )
let data = ( await testResponse.json( ) ).data
document.getElementById( 'serverDetails' ).innerHTML = `<b>${data.serverInfo.name}</b> deployed by <b>${data.serverInfo.company}</b>`
}
function logout( ) {
localStorage.removeItem( 'AuthToken' )
localStorage.removeItem( 'RefreshToken' )
window.location = '/explorer'
}
window.addEventListener( 'load', async function ( event ) {
let urlParams = new URLSearchParams( window.location.search )
let accessCode = urlParams.get( 'access_code' )
if ( accessCode ) {
accessCodeExchange( accessCode )
} else {
let token = localStorage.getItem( 'AuthToken' )
if ( token ) {
let testResponse = await fetch( '/graphql', {
method: 'POST',
headers: {
'Authorization': 'Bearer ' + token,
'Content-Type': 'application/json'
},
body: JSON.stringify( { query: `{ user { name } }` } )
} )
let data = ( await testResponse.json( ) ).data
// if res.data.user is non null, means the ping was ok & token is valid
if ( data.user ) {
console.log( data.user )
document.getElementById( 'username' ).innerHTML = data.user.name
await initGQL( token )
}
} else {
redirectToAuth( )
}
}
} )
</script>
let data = await response.json( )
if ( data.hasOwnProperty( 'token' ) ) {
localStorage.removeItem( 'challenge' )
localStorage.setItem( 'AuthToken', data.token )
localStorage.setItem( 'RefreshToken', data.refreshToken )
await setUserName( )
await initGQL( data.token )
}
}
async function initGQL( token ) {
// GraphQLPlayground.init( document.getElementById( 'root' ), {
// endpointUrl: '/graphql',
// headers: {
// 'Authorization': 'Bearer ' + token
// }
// } )
ReactDOM.render(
React.createElement( GraphiQL, { fetcher: graphQLFetcher } ),
document.getElementById( 'graphiql' ),
)
await setServerInfo( )
}
async function setUserName( ) {
let testResponse = await fetch( '/graphql', {
method: 'POST',
headers: {
'Authorization': 'Bearer ' + localStorage.getItem( 'AuthToken' ),
'Content-Type': 'application/json'
},
body: JSON.stringify( { query: `{ user { name } }` } )
} )
let data = ( await testResponse.json( ) ).data
document.getElementById( 'username' ).innerHTML = data.user.name
}
async function setServerInfo( ) {
let testResponse = await fetch( '/graphql', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify( { query: `{ serverInfo { name company } }` } )
} )
let data = ( await testResponse.json( ) ).data
document.getElementById( 'serverDetails' ).innerHTML = `<b>${data.serverInfo.name}</b> deployed by <b>${data.serverInfo.company}</b>`
}
function logout( ) {
localStorage.removeItem( 'AuthToken' )
localStorage.removeItem( 'RefreshToken' )
window.location = '/explorer'
}
window.addEventListener( 'load', async function ( event ) {
let urlParams = new URLSearchParams( window.location.search )
let accessCode = urlParams.get( 'access_code' )
if ( accessCode ) {
accessCodeExchange( accessCode )
} else {
let token = localStorage.getItem( 'AuthToken' )
if ( token ) {
let testResponse = await fetch( '/graphql', {
method: 'POST',
headers: {
'Authorization': 'Bearer ' + token,
'Content-Type': 'application/json'
},
body: JSON.stringify( { query: '{ user { name } }' } )
} )
let data = ( await testResponse.json( ) ).data
// if res.data.user is non null, means the ping was ok & token is valid
if ( data.user ) {
console.log( data.user )
document.getElementById( 'username' ).innerHTML = data.user.name
await initGQL( token )
}
} else {
redirectToAuth( )
}
}
} )
</script>
</body>
<style>
#speckle-header {
@@ -437,4 +437,4 @@ li.CodeMirror-hint-active {
}
</style>
</html>
</html>
+1
View File
@@ -38,6 +38,7 @@ type ServerAppListItem {
type AppAuthor {
name: String
id: String
avatar: String
}
extend type User {
+95 -68
View File
@@ -8,8 +8,10 @@ const passport = require( 'passport' )
const debug = require( 'debug' )
const sentry = require( `${appRoot}/logging/sentryHelper` )
const { getApp, createAuthorizationCode, createAppTokenFromAccessCode, refreshAppToken } = require( './services/apps' )
const { createPersonalAccessToken } = require( `${appRoot}/modules/core/services/tokens` )
const { getApp, getAllAppsAuthorizedByUser, createAuthorizationCode, createAppTokenFromAccessCode, refreshAppToken } = require( './services/apps' )
const { createPersonalAccessToken, validateToken, revokeTokenById } = require( `${appRoot}/modules/core/services/tokens` )
const { revokeRefreshToken } = require( `${appRoot}/modules/auth/services/apps` )
const { validateScopes, contextMiddleware } = require( `${appRoot}/modules/shared` )
let authStrategies = [ ]
@@ -31,61 +33,95 @@ exports.init = ( app, options ) => {
cookie: { maxAge: 1000 * 60 * 3 } // 3 minutes
} )
let sessionAppId = ( req, res, next ) => {
req.session.appId = req.query.appId
let sessionStorage = ( req, res, next ) => {
req.session.challenge = req.query.challenge
if ( req.query.suuid ) {
req.session.suuid = req.query.suuid
}
next( )
}
/*
Finalizes authentication for the main frontend application.
*/
let finalizeAuth = async ( req, res, next ) => {
if ( req.session.appId ) {
try {
let app = await getApp( { id: req.session.appId } )
let ac = await createAuthorizationCode( { appId: app.id, userId: req.user.id, challenge: req.session.challenge } )
if ( req.session ) req.session.destroy( )
return res.redirect( `/auth/finalize?appId=${app.id}&access_code=${ac}` )
} catch ( err ) {
sentry( { err } )
if ( req.session ) req.session.destroy( )
return res.status( 401 ).send( 'Invalid request.' )
}
} else {
if ( process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'test' ) {
let token = await createPersonalAccessToken( req.user.id, 'test token', [ 'streams:write', 'streams:read', 'profile:read', 'profile:email', 'users:read', 'users:email' ] )
if ( req.session ) req.session.destroy( )
return res.status( 200 ).send( { userId: req.user.id, apiToken: token } )
}
try {
let app = await getApp( { id: 'spklwebapp' } )
let ac = await createAuthorizationCode( { appId: 'spklwebapp', userId: req.user.id, challenge: req.session.challenge } )
if ( req.session ) req.session.destroy( )
return res.status( 200 ).end( )
return res.redirect( `${app.redirectUrl}?access_code=${ac}` )
} catch ( err ) {
sentry( { err } )
if ( req.session ) req.session.destroy( )
return res.status( 401 ).send( 'Invalid request.' )
}
}
// TODO: add cors
app.post( '/auth/token', async ( req, res, next ) => {
/*
Strategies initialisation & listing
*/
let strategyCount = 0
if ( process.env.STRATEGY_GOOGLE === 'true' ) {
let googStrategy = require( './strategies/google' )( app, session, sessionStorage, finalizeAuth )
authStrategies.push( googStrategy )
strategyCount++
}
if ( process.env.STRATEGY_GITHUB === 'true' ) {
let githubStrategy = require( './strategies/github' )( app, session, sessionStorage, finalizeAuth )
authStrategies.push( githubStrategy )
strategyCount++
}
// Note: always leave the local strategy init for last so as to be able to
// force enable it in case no others are present.
if ( process.env.STRATEGY_LOCAL === 'true' || strategyCount === 0 ) {
let localStrategy = require( './strategies/local' )( app, session, sessionStorage, finalizeAuth )
authStrategies.push( localStrategy )
}
/*
Auth routes
*/
/*
Generates an access code for an app.
*/
app.get( '/auth/accesscode', async( req, res, next ) => {
try {
let appId = req.query.appId
let app = await getApp( { id: appId } )
if ( !app ) throw new Error( 'App does not exist.' )
let challenge = req.query.challenge
let userToken = req.query.token
// 1. Validate token
let { valid, scopes, userId, role } = await validateToken( userToken )
if ( !valid ) throw new Error( 'Invalid token' )
// 2. Validate token scopes
await validateScopes( scopes, 'tokens:write' )
let ac = await createAuthorizationCode( { appId, userId, challenge } )
return res.redirect( `${app.redirectUrl}?access_code=${ac}` )
} catch ( err ) {
sentry( { err } )
debug( 'speckle:errors' )( err )
return res.status( 400 ).send( err.message )
}
} )
/*
Generates a new api token: (1) either via a valid refresh token or (2) via a valid access token
*/
app.post( '/auth/token', async ( req, res, next ) => {
try {
// Token refresh
if ( req.body.refreshToken ) {
if ( !req.body.appId || !req.body.appSecret )
throw new Error( 'Invalid request - refresh token' )
@@ -94,46 +130,37 @@ exports.init = ( app, options ) => {
return res.send( authResponse )
}
// Access-code - token exchange
if ( !req.body.appId || !req.body.appSecret || !req.body.accessCode || !req.body.challenge )
throw new Error( 'Invalid request' + JSON.stringify( req.body ) )
let authResponse = await createAppTokenFromAccessCode( { appId: req.body.appId, appSecret: req.body.appSecret, accessCode: req.body.accessCode, challenge: req.body.challenge } )
return res.send( authResponse )
} catch ( err ) {
sentry( { err } )
return res.status( 401 ).send( { err: err.message } )
}
} )
// TODO: add logout route
/*
Ensures a user is logged out by invalidating their token and refresh token.
*/
app.post( '/auth/logout', async ( req, res, next ) => {
try {
let token = req.body.token
let refreshToken = req.body.refreshToken
if ( !token ) throw new Error( 'Invalid request' )
await revokeTokenById( token )
// Strategies initialisation & listing
// NOTE: if no strategies are defined, the local one will be enabled.
if ( refreshToken )
revokeRefreshToken( { tokenId:refreshToken } )
let strategyCount = 0
if ( process.env.STRATEGY_GITHUB === 'true' ) {
let githubStrategy = require( './strategies/github' )( app, session, sessionAppId, finalizeAuth )
authStrategies.push( githubStrategy )
strategyCount++
}
if ( process.env.STRATEGY_GOOGLE === 'true' ) {
let googStrategy = require( './strategies/google' )( app, session, sessionAppId, finalizeAuth )
authStrategies.push( googStrategy )
strategyCount++
}
// Note: always leave the local strategy init for last so as to be able to
// force enable it in case no others are present.
if ( process.env.STRATEGY_LOCAL === 'true' || strategyCount === 0 ) {
let localStrategy = require( './strategies/local' )( app, session, sessionAppId, finalizeAuth )
authStrategies.push( localStrategy )
}
return res.status( 200 ).send( { message: 'You have logged out.' } )
} catch ( err ){
sentry( { err } )
return res.status( 400 ).send( { err: err.message } )
}
} )
}
@@ -20,10 +20,9 @@ exports.up = async knex => {
const desktopConnectorScopes = [
{ appId: 'sdm', scopeName: 'streams:read' },
{ appId: 'sdm', scopeName: 'streams:write' },
{ appId: 'sdm', scopeName: 'profile:read' },
{ appId: 'sdm', scopeName: 'profile:email' },
{ appId: 'sdm', scopeName: 'users:read' },
{ appId: 'sdm', scopeName: 'users:read' }
]
await knex( 'server_apps_scopes' ).insert( desktopConnectorScopes )
@@ -39,7 +38,7 @@ exports.up = async knex => {
`,
trustByDefault: true,
public: true,
redirectUrl: 'self'
redirectUrl: process.env.CANONICAL_URL
} )
const scopes = await knex( 'scopes' ).select( '*' )
@@ -56,7 +55,7 @@ exports.up = async knex => {
description: 'GraphQL Playground with authentication.',
trustByDefault: true,
public: true,
redirectUrl: '/explorer',
redirectUrl: `${process.env.CANONICAL_URL}/explorer`
} )
const explorerScopes = scopes.filter( s => s.name !== 'server:setup' ).map( s => ( { appId: 'explorer', scopeName: s.name } ) )
@@ -70,10 +69,10 @@ exports.up = async knex => {
secret: '12345',
name: 'Mock Application',
description: 'Lorem ipsum dolor sic amet.',
redirectUrl: 'http://localhost:1337', // ie, will just redirect to window.location
redirectUrl: 'http://localhost:1337'
} )
const mockAppScopes = [ { appId: 'mock', scopeName: 'streams:read' }, { appId: 'mock', scopeName: 'users:read' }, { appId: 'mock', scopeName: 'profile:email' } ]
const mockAppScopes = [ { appId: 'mock', scopeName: 'streams:read' }, { appId: 'mock', scopeName: 'streams:write' }, { appId: 'mock', scopeName: 'users:read' }, { appId: 'mock', scopeName: 'profile:email' } ]
await knex( 'server_apps_scopes' ).insert( mockAppScopes )
}
+10 -1
View File
@@ -28,7 +28,7 @@ module.exports = {
let appScopeNames = ( await ServerAppsScopes( ).select( 'scopeName' ).where( { appId: id } ) ).map( s => s.scopeName )
app.scopes = allScopes.filter( scope => appScopeNames.indexOf( scope.name ) !== -1 )
app.author = await Users( ).select( 'id', 'name' ).where( { id: app.authorId } ).first( )
app.author = await Users( ).select( 'id', 'name', 'avatar' ).where( { id: app.authorId } ).first( )
return app
},
@@ -134,6 +134,15 @@ module.exports = {
},
async revokeRefreshToken( { tokenId } ) {
tokenId = tokenId.slice( 0, 10 )
let delCount = await RefreshTokens( ).where( { id: tokenId } ).del( )
if ( delCount === 0 )
throw new Error( 'Did not revoke token' )
return true
},
async revokeExistingAppCredentials( { appId } ) {
let resAccessCodeDelete = await AuthorizationCodes( ).where( { appId: appId } ).del( )
+5 -5
View File
@@ -8,13 +8,13 @@ const appRoot = require( 'app-root-path' )
const { findOrCreateUser } = require( `${appRoot}/modules/core/services/users` )
const { getApp, createAuthorizationCode, createAppTokenFromAccessCode } = require( '../services/apps' )
module.exports = ( app, session, sessionAppId, finalizeAuth ) => {
module.exports = ( app, session, sessionStorage, finalizeAuth ) => {
const strategy = {
id: 'github',
name: 'Github',
icon: 'TODO',
color: 'grey darken-2',
url: `/auth/gh`,
icon: 'mdi-github',
color: 'grey darken-3',
url: '/auth/gh',
callbackUrl: ( new URL( '/auth/gh/callback', process.env.CANONICAL_URL ) ).toString( )
}
@@ -41,7 +41,7 @@ module.exports = ( app, session, sessionAppId, finalizeAuth ) => {
passport.use( myStrategy )
app.get( strategy.url, session, sessionAppId, passport.authenticate( 'github', { failureRedirect: '/auth/error' } ) )
app.get( strategy.url, session, sessionStorage, passport.authenticate( 'github', { failureRedirect: '/auth/error' } ) )
app.get( '/auth/gh/callback', session, passport.authenticate( 'github', { failureRedirect: '/auth/error' } ), finalizeAuth )
return strategy
+4 -4
View File
@@ -7,12 +7,12 @@ const appRoot = require( 'app-root-path' )
const { findOrCreateUser } = require( `${appRoot}/modules/core/services/users` )
const { getApp, createAuthorizationCode, createAppTokenFromAccessCode } = require( '../services/apps' )
module.exports = ( app, session, sessionAppId, finalizeAuth ) => {
module.exports = ( app, session, sessionStorage, finalizeAuth ) => {
const strategy = {
id: 'google',
name: 'Google',
icon: 'TODO',
color: 'white red--text',
icon: 'mdi-google',
color: 'red darken-3',
url: '/auth/goog',
callbackUrl: '/auth/goog/callback'
}
@@ -40,7 +40,7 @@ module.exports = ( app, session, sessionAppId, finalizeAuth ) => {
passport.use( myStrategy )
app.get( strategy.url, session, sessionAppId, passport.authenticate( 'google' ) )
app.get( strategy.url, session, sessionStorage, passport.authenticate( 'google' ) )
app.get( '/auth/goog/callback', session, passport.authenticate( 'google', { failureRedirect: '/auth/error' } ), finalizeAuth )
return strategy
+16 -13
View File
@@ -12,25 +12,28 @@ module.exports = ( app, session, sessionAppId, finalizeAuth ) => {
name: 'Local',
icon: 'TODO',
color: 'accent',
url: `/auth/local`
url: '/auth/local'
}
app.post( '/auth/local/login', session, sessionAppId, async ( req, res, next ) => {
let valid = await validatePasssword( { email: req.body.email, password: req.body.password } )
try {
let valid = await validatePasssword( { email: req.body.email, password: req.body.password } )
if ( !valid ) {
if ( !valid ) throw new Error( 'Invalid credentials' )
let user = await getUserByEmail( { email: req.body.email } )
if ( !user ) throw new Error( 'Invalid credentials' )
if ( req.body.suuid && user.suuid !== req.body.suuid ) {
await updateUser( user.id, { suuid: req.body.suuid } )
}
req.user = { id: user.id }
next( )
} catch ( err ){
return res.status( 401 ).send( { err: true, message: 'Invalid credentials' } )
}
let user = await getUserByEmail( { email: req.body.email } )
if ( req.body.suuid && user.suuid !== req.body.suuid ) {
await updateUser( user.id, { suuid: req.body.suuid } )
}
req.user = { id: user.id }
next( )
}, finalizeAuth )
app.post( '/auth/local/register', session, sessionAppId, async ( req, res, next ) => {
@@ -5,26 +5,26 @@ let debug = require( 'debug' )( 'speckle:modules' )
exports.up = async knex => {
debug( 'Setting up core module scopes.' )
let coreModuleScopes = [
let coreModuleScopes = [
{
name: 'streams:read',
description: 'Read your streams & and any associated information (branches, tags, comments, objects, etc.)'
description: 'Read your streams, and any associated information (branches, commits, objects).'
},
{
name: 'streams:write',
description: 'Create streams on your behalf and read your streams & any associated information (any associated information (branches, tags, comments, objects, etc.)'
description: 'Create streams on your behalf, and any associated data (branches, commits, objects).'
},
{
name: 'profile:read',
description: `Read your profile information.`
description: 'Read your profile information (name, bio, company).'
},
{
name: 'profile:email',
description: `Access your email.`
description: 'Grants access to the email address you registered with.'
},
{
name: 'users:read',
description: `Read other users' profile on your behalf.`
description: 'Read other users\' profile on your behalf.'
},
{
name: 'users:email',
@@ -36,12 +36,12 @@ exports.up = async knex => {
},
{
name: 'tokens:read',
description: `Access your api tokens.`
description: 'Access your api tokens.'
},
{
name: 'tokens:write',
description: `Create and delete api tokens on your behalf.`
}]
description: 'Create and delete api tokens on your behalf.'
} ]
await knex( 'scopes' ).insert( coreModuleScopes )
}
+6 -6
View File
@@ -11,7 +11,7 @@ const { createObjects, createObjectsBatched } = require( '../services/objects' )
module.exports = ( app ) => {
app.post( '/objects/:streamId', contextMiddleware, async ( req, res ) => {
debug( 'speckle:upload-endpoint' )( `booom upload endpoint` )
debug( 'speckle:upload-endpoint' )( 'booom upload endpoint' )
if ( !req.context || !req.context.auth ) {
return res.status( 401 ).end( )
@@ -29,7 +29,7 @@ module.exports = ( app ) => {
return res.status( 401 ).end( )
}
debug( 'speckle:upload-endpoint' )( `Upload started` )
debug( 'speckle:upload-endpoint' )( 'Upload started' )
let busboy = new Busboy( { headers: req.headers } )
let totalProcessed = 0
@@ -57,7 +57,7 @@ module.exports = ( app ) => {
objs = JSON.parse( gunzipedBuffer )
} catch ( e ) {
requestDropped = true
return res.status( 400 ).send( `Failed to parse data.` )
return res.status( 400 ).send( 'Failed to parse data.' )
}
last = objs[ objs.length - 1 ]
@@ -82,7 +82,7 @@ module.exports = ( app ) => {
objs = JSON.parse( buffer )
} catch ( e ) {
requestDropped = true
return res.status( 400 ).send( `Failed to parse data.` )
return res.status( 400 ).send( 'Failed to parse data.' )
}
last = objs[ objs.length - 1 ]
totalProcessed += objs.length
@@ -94,7 +94,7 @@ module.exports = ( app ) => {
} )
} else {
requestDropped = true
return res.status( 400 ).send( `Invalid ContentType header. This route only accepts "application/gzip", "text/plain" or "application/json".` )
return res.status( 400 ).send( 'Invalid ContentType header. This route only accepts "application/gzip", "text/plain" or "application/json".' )
}
} )
@@ -105,7 +105,7 @@ module.exports = ( app ) => {
await Promise.all( promises )
debug( 'speckle:upload-endpoint' )( `Upload ended` )
debug( 'speckle:upload-endpoint' )( 'Upload ended' )
res.status( 201 ).end( )
} )
-1
View File
@@ -92,7 +92,6 @@ module.exports = {
async revokeToken( tokenId, userId ) {
tokenId = tokenId.slice( 0, 10 )
let token = await ApiTokens( ).where( { id: tokenId } ).select( "*" )
let delCount = await ApiTokens( ).where( { id: tokenId, owner: userId } ).del( )
if ( delCount === 0 )
+2 -2
View File
@@ -29,8 +29,8 @@ async function contextApiTokenHelper( { req, res, connection } ) {
token = req.headers.authorization
}
if ( token && token.includes( "Bearer " ) ) {
token = token.split( " " )[ 1 ]
if ( token && token.includes( 'Bearer ' ) ) {
token = token.split( ' ' )[ 1 ]
}
if ( token === null )