Merge pull request #4 from Speckle-Next/dim/streaming-endpoint

fix(local authn): fixes some uncaught errs
This commit is contained in:
Dimitrie Stefanescu
2020-07-15 10:55:32 +01:00
committed by GitHub
8 changed files with 156 additions and 132 deletions
+3
View File
@@ -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
-21
View File
@@ -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
}
}
}
-23
View File
@@ -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
View File
@@ -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
View File
@@ -1,3 +1,4 @@
/* istanbul ignore file */
'use strict'
const passport = require( 'passport' )
+1
View File
@@ -1,3 +1,4 @@
/* istanbul ignore file */
'use strict'
const passport = require( 'passport' )
const GoogleStrategy = require( 'passport-google-oauth20' ).Strategy
+3
View File
@@ -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
View File
@@ -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 )
} )
} )