Merge pull request #4 from Speckle-Next/dim/streaming-endpoint
fix(local authn): fixes some uncaught errs
This commit is contained in:
@@ -21,6 +21,9 @@ jobs:
|
||||
DATABASE_URL: 'postgres://speckle:speckle@localhost:5432/speckle2_test'
|
||||
PGDATABASE: speckle2_test
|
||||
PGUSER: speckle
|
||||
SESSION_SECRET: 'keyboard cat'
|
||||
STRATEGY_LOCAL: true
|
||||
CANONICAL_URL: 'http://localhost:3000'
|
||||
|
||||
steps:
|
||||
- checkout
|
||||
|
||||
@@ -19,26 +19,5 @@ module.exports = {
|
||||
}
|
||||
},
|
||||
Mutation: {
|
||||
async appAuthorize( parent, args, context, info ) {
|
||||
await validateServerRole( context, 'server:user' )
|
||||
await validateScopes( context.scopes, 'apps:authorize' ) // TODO
|
||||
|
||||
// Implicit grant flow: returns the token directly
|
||||
// let token = await createAppToken( { userId: context.userId, appId: args.appId } )
|
||||
// return token
|
||||
|
||||
// TODO: Implement authorization code grant
|
||||
let accessCode = await createAuthorizationCode( { userId: contex.userId, appId: args.appId, challenge: args.challenge } )
|
||||
return accessCode
|
||||
},
|
||||
async appGetToken( parent, args, context, info ) {
|
||||
|
||||
let result = await exchangeAuthorizationCodeForToken( { appId: args.appId, appSecret: args.appSecret, accessCode: args.accessCode, challenge: args.challenge } )
|
||||
// args.appId, args.appSecret, args.accessCode
|
||||
|
||||
},
|
||||
async appRefreshToken( parent, args, context, info ) {
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -31,26 +31,3 @@ type AuthStrategy {
|
||||
url: String!,
|
||||
color: String
|
||||
}
|
||||
extend type Mutation {
|
||||
"""
|
||||
Authorizes an app on behalf of a user. Returns an access code that can be exchanged
|
||||
by the application for an api token.
|
||||
"""
|
||||
appAuthorize( appId: String!, challenge: String! ): String!
|
||||
"""
|
||||
Exchanges an access code for an api token.
|
||||
"""
|
||||
appGetToken( appId: String!, appSecret: String!, accesCode: String!, challenge: String! ): AppTokenResponse!
|
||||
"""
|
||||
Refreshes an expired token.
|
||||
"""
|
||||
appRefreshToken( appId: String, appSecret:String!, refreshToken: String! ): AppTokenResponse!
|
||||
}
|
||||
|
||||
type AppTokenResponse {
|
||||
"""
|
||||
The actual bearer token.
|
||||
"""
|
||||
token: String!
|
||||
refreshToken: String!
|
||||
}
|
||||
|
||||
+20
-8
@@ -35,9 +35,17 @@ exports.init = ( app, options ) => {
|
||||
}
|
||||
|
||||
let finalizeAuth = async ( req, res, next ) => {
|
||||
let app = await getApp( { id: req.session.appId } )
|
||||
let ac = await createAuthorizationCode( { appId: app.id, userId: req.user.id, challenge: req.session.challenge } )
|
||||
return res.redirect( `/auth/finalize?appId=${req.session.appId}&access_code=${ac}` )
|
||||
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 } )
|
||||
return res.redirect( `/auth/finalize?appId=${req.session.appId}&access_code=${ac}` )
|
||||
} catch ( err ) {
|
||||
return res.status( 400 ).send( err.message )
|
||||
}
|
||||
} else {
|
||||
return res.status( 200 ).end( )
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: add cors
|
||||
@@ -66,13 +74,17 @@ exports.init = ( app, options ) => {
|
||||
|
||||
// Strategies initialisation & listing
|
||||
|
||||
let githubStrategy = require( './strategies/github' )( app, session, sessionAppId, finalizeAuth )
|
||||
authStrategies.push( githubStrategy )
|
||||
if ( process.env.STRATEGY_GITHUB === 'true' ) {
|
||||
let githubStrategy = require( './strategies/github' )( app, session, sessionAppId, finalizeAuth )
|
||||
authStrategies.push( githubStrategy )
|
||||
}
|
||||
|
||||
let googStrategy = require( './strategies/google' )( app, session, sessionAppId, finalizeAuth )
|
||||
authStrategies.push( googStrategy )
|
||||
if ( process.env.STRATEGY_GOOGLE === 'true' ) {
|
||||
let googStrategy = require( './strategies/google' )( app, session, sessionAppId, finalizeAuth )
|
||||
authStrategies.push( googStrategy )
|
||||
}
|
||||
|
||||
if ( process.env.STRATEGY_LOCAL ) {
|
||||
if ( process.env.STRATEGY_LOCAL === 'true' ) {
|
||||
let localStrategy = require( './strategies/local' )( app, session, sessionAppId, finalizeAuth )
|
||||
authStrategies.push( localStrategy )
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* istanbul ignore file */
|
||||
'use strict'
|
||||
|
||||
const passport = require( 'passport' )
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* istanbul ignore file */
|
||||
'use strict'
|
||||
const passport = require( 'passport' )
|
||||
const GoogleStrategy = require( 'passport-google-oauth20' ).Strategy
|
||||
|
||||
@@ -28,6 +28,9 @@ module.exports = ( app, session, sessionAppId, finalizeAuth ) => {
|
||||
|
||||
app.post( '/auth/local/register', session, sessionAppId, async ( req, res, next ) => {
|
||||
try {
|
||||
if ( !req.body.password )
|
||||
throw new Error( 'Password missing' )
|
||||
|
||||
let userId = await createUser( req.body )
|
||||
req.user = { id: userId }
|
||||
return next( )
|
||||
|
||||
+128
-80
@@ -1,5 +1,6 @@
|
||||
const chai = require( 'chai' )
|
||||
const chaiHttp = require( 'chai-http' )
|
||||
const request = require( 'supertest' )
|
||||
const assert = require( 'assert' )
|
||||
const appRoot = require( 'app-root-path' )
|
||||
|
||||
@@ -17,89 +18,136 @@ const { getApp, registerApp, createAuthorizationCode, createAppTokenFromAccessCo
|
||||
|
||||
describe( 'Apps', ( ) => {
|
||||
|
||||
let actor = {
|
||||
username: 'DimitrieStefanescu',
|
||||
name: 'Dimitrie Stefanescu',
|
||||
email: 'didimitrie@gmail.com',
|
||||
password: 'wtfwtfwtf'
|
||||
}
|
||||
describe( 'Services', ( ) => {
|
||||
let actor = {
|
||||
username: 'DimitrieStefanescu',
|
||||
name: 'Dimitrie Stefanescu',
|
||||
email: 'didimitrie@gmail.com',
|
||||
password: 'wtfwtfwtf'
|
||||
}
|
||||
|
||||
before( async ( ) => {
|
||||
await knex.migrate.rollback( )
|
||||
await knex.migrate.latest( )
|
||||
actor.id = await createUser( actor )
|
||||
before( async ( ) => {
|
||||
await knex.migrate.rollback( )
|
||||
await knex.migrate.latest( )
|
||||
actor.id = await createUser( actor )
|
||||
} )
|
||||
|
||||
after( async ( ) => {
|
||||
|
||||
} )
|
||||
|
||||
it( 'Should get the frontend main app', async ( ) => {
|
||||
let app = await getApp( { id: 'spklwebapp' } )
|
||||
expect( app ).to.be.an( 'object' )
|
||||
expect( app.redirectUrl ).to.be.a( 'string' )
|
||||
expect( app.scopes ).to.be.a( 'array' )
|
||||
expect( app.firstparty ).to.equal( true )
|
||||
} )
|
||||
|
||||
it( 'Should get the mock app', async ( ) => {
|
||||
let app = await getApp( { id: 'mock' } )
|
||||
expect( app ).to.be.an( 'object' )
|
||||
expect( app.redirectUrl ).to.be.a( 'string' )
|
||||
expect( app.scopes ).to.be.a( 'array' )
|
||||
expect( app.firstparty ).to.equal( false )
|
||||
} )
|
||||
|
||||
let myTestApp = null
|
||||
|
||||
it( 'Should register an app', async ( ) => {
|
||||
let res = await registerApp( { name: 'test application', firstparty: true, author: actor.id, scopes: [ 'streams:read' ], redirectUrl: 'http://localhost:1335' } )
|
||||
|
||||
expect( res ).to.have.property( 'id' )
|
||||
expect( res ).to.have.property( 'secret' )
|
||||
|
||||
expect( res.id ).to.be.a( 'string' )
|
||||
expect( res.secret ).to.be.a( 'string' )
|
||||
myTestApp = res
|
||||
|
||||
let app = await getApp( { id: res.id } )
|
||||
expect( app.firstparty ).to.equal( false )
|
||||
expect( app.id ).to.equal( res.id )
|
||||
} )
|
||||
|
||||
let challenge = 'random'
|
||||
let authorizationCode = null
|
||||
it( 'Should get an authorization code for the app', async ( ) => {
|
||||
authorizationCode = await createAuthorizationCode( { appId: myTestApp.id, userId: actor.id, challenge } )
|
||||
expect( authorizationCode ).to.be.a( 'string' )
|
||||
} )
|
||||
|
||||
let tokenCreateResponse = null
|
||||
it( 'Should get an api token in exchange for the authorization code ', async ( ) => {
|
||||
let response = await createAppTokenFromAccessCode( { appId: myTestApp.id, appSecret: myTestApp.secret, accessCode: authorizationCode, challenge: 'random' } )
|
||||
expect( response ).to.have.property( 'token' )
|
||||
expect( response.token ).to.be.a( 'string' )
|
||||
expect( response ).to.have.property( 'refreshToken' )
|
||||
expect( response.refreshToken ).to.be.a( 'string' )
|
||||
|
||||
tokenCreateResponse = response
|
||||
|
||||
let validation = await validateToken( response.token )
|
||||
expect( validation.valid ).to.equal( true )
|
||||
expect( validation.userId ).to.equal( actor.id )
|
||||
expect( validation.scopes[ 0 ] ).to.equal( 'streams:read' )
|
||||
} )
|
||||
|
||||
it( 'Should refresh the token using the refresh token, and get a fresh refresh token and token', async ( ) => {
|
||||
let res = await refreshAppToken( { refreshToken: tokenCreateResponse.refreshToken, appId: myTestApp.id, appSecret: myTestApp.secret, userId: actor.id } )
|
||||
|
||||
expect( res.token ).to.be.a( 'string' )
|
||||
expect( res.refreshToken ).to.be.a( 'string' )
|
||||
|
||||
let validation = await validateToken( res.token )
|
||||
expect( validation.valid ).to.equal( true )
|
||||
expect( validation.userId ).to.equal( actor.id )
|
||||
} )
|
||||
} )
|
||||
|
||||
after( async ( ) => {
|
||||
describe( 'Local authN', ( ) => {
|
||||
let expressApp
|
||||
before( async ( ) => {
|
||||
await knex.migrate.rollback( )
|
||||
await knex.migrate.latest( )
|
||||
|
||||
let { app } = await init( )
|
||||
expressApp = app
|
||||
} )
|
||||
|
||||
after( async ( ) => {
|
||||
await knex.migrate.rollback( )
|
||||
} )
|
||||
|
||||
it( 'Should register a new user', async ( ) => {
|
||||
let res =
|
||||
await request( expressApp )
|
||||
.post( `/auth/local/register` )
|
||||
.send( { email: 'spam@speckle.systems', name: 'dimitrie stefanescu', username: 'dimitrie', company: 'speckle', password: 'roll saving throws' } )
|
||||
.expect( 200 )
|
||||
} )
|
||||
|
||||
it( 'Should fail to register a new user w/o password', async ( ) => {
|
||||
let res =
|
||||
await request( expressApp )
|
||||
.post( `/auth/local/register` )
|
||||
.send( { email: 'spam@speckle.systems', name: 'dimitrie stefanescu', username: 'dimitrie' } )
|
||||
.expect( 400 )
|
||||
} )
|
||||
|
||||
it( 'Should log in ', async ( ) => {
|
||||
let res =
|
||||
await request( expressApp )
|
||||
.post( `/auth/local/login` )
|
||||
.send( { email: 'spam@speckle.systems', password: 'roll saving throws' } )
|
||||
.expect( 200 )
|
||||
} )
|
||||
|
||||
it( 'Should fail nicely to log in ', async ( ) => {
|
||||
let res =
|
||||
await request( expressApp )
|
||||
.post( `/auth/local/login` )
|
||||
.send( { email: 'spam@speckle.systems', password: 'roll saving throw' } )
|
||||
.expect( 401 )
|
||||
} )
|
||||
} )
|
||||
|
||||
it( 'Should get the frontend main app', async ( ) => {
|
||||
let app = await getApp( { id: 'spklwebapp' } )
|
||||
expect( app ).to.be.an( 'object' )
|
||||
expect( app.redirectUrl ).to.be.a( 'string' )
|
||||
expect( app.scopes ).to.be.a( 'array' )
|
||||
expect( app.firstparty ).to.equal( true )
|
||||
} )
|
||||
|
||||
it( 'Should get the mock app', async ( ) => {
|
||||
let app = await getApp( { id: 'mock' } )
|
||||
expect( app ).to.be.an( 'object' )
|
||||
expect( app.redirectUrl ).to.be.a( 'string' )
|
||||
expect( app.scopes ).to.be.a( 'array' )
|
||||
expect( app.firstparty ).to.equal( false )
|
||||
} )
|
||||
|
||||
let myTestApp = null
|
||||
|
||||
it( 'Should register an app', async ( ) => {
|
||||
let res = await registerApp( { name: 'test application', firstparty: true, author: actor.id, scopes: [ 'streams:read' ], redirectUrl: 'http://localhost:1335' } )
|
||||
|
||||
expect( res ).to.have.property( 'id' )
|
||||
expect( res ).to.have.property( 'secret' )
|
||||
|
||||
expect( res.id ).to.be.a( 'string' )
|
||||
expect( res.secret ).to.be.a( 'string' )
|
||||
myTestApp = res
|
||||
|
||||
let app = await getApp( { id: res.id } )
|
||||
expect( app.firstparty ).to.equal( false )
|
||||
expect( app.id ).to.equal( res.id )
|
||||
} )
|
||||
|
||||
let challenge = 'random'
|
||||
let authorizationCode = null
|
||||
it( 'Should get an authorization code for the app', async ( ) => {
|
||||
authorizationCode = await createAuthorizationCode( { appId: myTestApp.id, userId: actor.id, challenge } )
|
||||
expect( authorizationCode ).to.be.a( 'string' )
|
||||
} )
|
||||
|
||||
let tokenCreateResponse = null
|
||||
it( 'Should get an api token in exchange for the authorization code ', async ( ) => {
|
||||
let response = await createAppTokenFromAccessCode( { appId: myTestApp.id, appSecret: myTestApp.secret, accessCode: authorizationCode, challenge: 'random' } )
|
||||
expect( response ).to.have.property( 'token' )
|
||||
expect( response.token ).to.be.a( 'string' )
|
||||
expect( response ).to.have.property( 'refreshToken' )
|
||||
expect( response.refreshToken ).to.be.a( 'string' )
|
||||
|
||||
tokenCreateResponse = response
|
||||
|
||||
let validation = await validateToken( response.token )
|
||||
expect( validation.valid ).to.equal( true )
|
||||
expect( validation.userId ).to.equal( actor.id )
|
||||
expect( validation.scopes[ 0 ] ).to.equal( 'streams:read' )
|
||||
} )
|
||||
|
||||
it( 'Should refresh the token using the refresh token, and get a fresh refresh token and token', async ( ) => {
|
||||
let res = await refreshAppToken( { refreshToken: tokenCreateResponse.refreshToken, appId: myTestApp.id, appSecret: myTestApp.secret, userId: actor.id } )
|
||||
|
||||
expect( res.token ).to.be.a( 'string' )
|
||||
expect( res.refreshToken ).to.be.a( 'string' )
|
||||
|
||||
let validation = await validateToken( res.token )
|
||||
expect( validation.valid ).to.equal( true )
|
||||
expect( validation.userId ).to.equal( actor.id )
|
||||
} )
|
||||
|
||||
|
||||
} )
|
||||
Reference in New Issue
Block a user