2018-09-24 17:07:51 -04:00
const request = require ( '../lib/request' ) ;
2018-09-01 13:58:06 -04:00
const Config = require ( '../lib/Config' ) ;
2020-10-25 15:06:58 -05:00
const defaultColumns = require ( '../lib/Controllers/SchemaController' ) . defaultColumns ;
2018-07-02 23:30:14 -04:00
const authenticationLoader = require ( '../lib/Adapters/Auth' ) ;
2018-02-17 09:55:30 -05:00
const path = require ( 'path' ) ;
2016-02-04 14:03:39 -05:00
2020-07-15 18:56:08 +02:00
describe ( 'AuthenticationProviders' , function ( ) {
const getMockMyOauthProvider = function ( ) {
2016-02-04 14:03:39 -05:00
return {
authData : {
2018-09-01 13:58:06 -04:00
id : '12345' ,
access _token : '12345' ,
2016-02-04 14:03:39 -05:00
expiration _date : new Date ( ) . toJSON ( ) ,
} ,
shouldError : false ,
loggedOut : false ,
synchronizedUserId : null ,
synchronizedAuthToken : null ,
synchronizedExpiration : null ,
2020-07-15 18:56:08 +02:00
authenticate : function ( options ) {
2016-02-04 14:03:39 -05:00
if ( this . shouldError ) {
2018-09-01 13:58:06 -04:00
options . error ( this , 'An error occurred' ) ;
2016-02-04 14:03:39 -05:00
} else if ( this . shouldCancel ) {
options . error ( this , null ) ;
} else {
options . success ( this , this . authData ) ;
}
} ,
2020-07-15 18:56:08 +02:00
restoreAuthentication : function ( authData ) {
2016-02-04 14:03:39 -05:00
if ( ! authData ) {
this . synchronizedUserId = null ;
this . synchronizedAuthToken = null ;
this . synchronizedExpiration = null ;
return true ;
}
this . synchronizedUserId = authData . id ;
this . synchronizedAuthToken = authData . access _token ;
this . synchronizedExpiration = authData . expiration _date ;
return true ;
} ,
2020-07-15 18:56:08 +02:00
getAuthType : function ( ) {
2018-09-01 13:58:06 -04:00
return 'myoauth' ;
2016-02-04 14:03:39 -05:00
} ,
2020-07-15 18:56:08 +02:00
deauthenticate : function ( ) {
2016-02-04 14:03:39 -05:00
this . loggedOut = true ;
this . restoreAuthentication ( null ) ;
2018-09-01 13:58:06 -04:00
} ,
2016-02-04 14:03:39 -05:00
} ;
} ;
2016-11-24 15:47:41 -05:00
Parse . User . extend ( {
2020-07-15 18:56:08 +02:00
extended : function ( ) {
2016-02-04 14:03:39 -05:00
return true ;
2018-09-01 13:58:06 -04:00
} ,
2016-02-04 14:03:39 -05:00
} ) ;
2020-07-15 18:56:08 +02:00
const createOAuthUser = function ( callback ) {
2017-07-25 15:26:34 -04:00
return createOAuthUserWithSessionToken ( undefined , callback ) ;
2018-09-01 13:58:06 -04:00
} ;
2017-07-25 15:26:34 -04:00
2020-07-15 18:56:08 +02:00
const createOAuthUserWithSessionToken = function ( token , callback ) {
2018-02-17 09:55:30 -05:00
const jsonBody = {
2016-02-04 14:03:39 -05:00
authData : {
2018-09-01 13:58:06 -04:00
myoauth : getMockMyOauthProvider ( ) . authData ,
} ,
2016-02-04 14:03:39 -05:00
} ;
2018-02-17 09:55:30 -05:00
const options = {
2018-09-24 17:07:51 -04:00
method : 'POST' ,
2018-09-01 13:58:06 -04:00
headers : {
'X-Parse-Application-Id' : 'test' ,
2016-11-24 15:47:41 -05:00
'X-Parse-REST-API-Key' : 'rest' ,
'X-Parse-Installation-Id' : 'yolo' ,
2017-07-25 15:26:34 -04:00
'X-Parse-Session-Token' : token ,
2018-09-01 13:58:06 -04:00
'Content-Type' : 'application/json' ,
} ,
2016-11-24 15:47:41 -05:00
url : 'http://localhost:8378/1/users' ,
2017-07-25 15:26:34 -04:00
body : jsonBody ,
2016-11-24 15:47:41 -05:00
} ;
2018-09-24 17:07:51 -04:00
return request ( options )
2020-08-20 12:09:54 -05:00
. then ( response => {
2018-09-24 17:07:51 -04:00
if ( callback ) {
callback ( null , response , response . data ) ;
}
return {
res : response ,
body : response . data ,
} ;
} )
2020-08-20 12:09:54 -05:00
. catch ( error => {
2017-07-25 15:26:34 -04:00
if ( callback ) {
2018-09-24 17:07:51 -04:00
callback ( error ) ;
2017-07-25 15:26:34 -04:00
}
2018-09-24 17:07:51 -04:00
throw error ;
2017-07-25 15:26:34 -04:00
} ) ;
2018-09-01 13:58:06 -04:00
} ;
2016-02-04 14:03:39 -05:00
2020-08-20 12:09:54 -05:00
it ( 'should create user with REST API' , done => {
2016-02-04 14:03:39 -05:00
createOAuthUser ( ( error , response , body ) => {
2016-06-17 09:59:16 -07:00
expect ( error ) . toBe ( null ) ;
2018-02-17 09:55:30 -05:00
const b = body ;
2016-06-17 09:59:16 -07:00
ok ( b . sessionToken ) ;
expect ( b . objectId ) . not . toBeNull ( ) ;
expect ( b . objectId ) . not . toBeUndefined ( ) ;
2018-02-17 09:55:30 -05:00
const sessionToken = b . sessionToken ;
2018-09-01 13:58:06 -04:00
const q = new Parse . Query ( '_Session' ) ;
2016-06-17 09:59:16 -07:00
q . equalTo ( 'sessionToken' , sessionToken ) ;
2018-09-01 13:58:06 -04:00
q . first ( { useMasterKey : true } )
2020-08-20 12:09:54 -05:00
. then ( res => {
2018-09-01 13:58:06 -04:00
if ( ! res ) {
fail ( 'should not fail fetching the session' ) ;
done ( ) ;
return ;
}
expect ( res . get ( 'installationId' ) ) . toEqual ( 'yolo' ) ;
done ( ) ;
} )
. catch ( ( ) => {
2016-11-24 15:47:41 -05:00
fail ( 'should not fail fetching the session' ) ;
done ( ) ;
2018-09-01 13:58:06 -04:00
} ) ;
2016-06-17 09:59:16 -07:00
} ) ;
2016-02-04 14:03:39 -05:00
} ) ;
2020-08-20 12:09:54 -05:00
it ( 'should only create a single user with REST API' , done => {
2018-02-17 09:55:30 -05:00
let objectId ;
2016-02-04 14:03:39 -05:00
createOAuthUser ( ( error , response , body ) => {
2016-04-05 21:16:39 -07:00
expect ( error ) . toBe ( null ) ;
2018-09-01 13:58:06 -04:00
const b = body ;
2016-04-05 21:16:39 -07:00
expect ( b . objectId ) . not . toBeNull ( ) ;
expect ( b . objectId ) . not . toBeUndefined ( ) ;
objectId = b . objectId ;
createOAuthUser ( ( error , response , body ) => {
2016-02-04 14:03:39 -05:00
expect ( error ) . toBe ( null ) ;
2018-02-17 09:55:30 -05:00
const b = body ;
2016-02-04 14:03:39 -05:00
expect ( b . objectId ) . not . toBeNull ( ) ;
expect ( b . objectId ) . not . toBeUndefined ( ) ;
2016-04-05 21:16:39 -07:00
expect ( b . objectId ) . toBe ( objectId ) ;
done ( ) ;
2016-02-04 14:03:39 -05:00
} ) ;
2016-04-05 21:16:39 -07:00
} ) ;
2016-02-04 14:03:39 -05:00
} ) ;
2020-08-20 12:09:54 -05:00
it ( "should fail to link if session token don't match user" , done => {
2018-09-01 13:58:06 -04:00
Parse . User . signUp ( 'myUser' , 'password' )
2020-08-20 12:09:54 -05:00
. then ( user => {
2018-09-01 13:58:06 -04:00
return createOAuthUserWithSessionToken ( user . getSessionToken ( ) ) ;
} )
. then ( ( ) => {
return Parse . User . logOut ( ) ;
} )
. then ( ( ) => {
return Parse . User . signUp ( 'myUser2' , 'password' ) ;
} )
2020-08-20 12:09:54 -05:00
. then ( user => {
2018-09-01 13:58:06 -04:00
return createOAuthUserWithSessionToken ( user . getSessionToken ( ) ) ;
} )
2018-09-24 17:07:51 -04:00
. then ( fail , ( { data } ) => {
expect ( data . code ) . toBe ( 208 ) ;
expect ( data . error ) . toBe ( 'this auth is already used' ) ;
2018-09-01 13:58:06 -04:00
done ( ) ;
} )
. catch ( done . fail ) ;
2017-07-25 15:26:34 -04:00
} ) ;
2022-11-11 03:35:39 +11:00
it ( 'should support loginWith with session token and with/without mutated authData' , async ( ) => {
const fakeAuthProvider = {
validateAppId : ( ) => Promise . resolve ( ) ,
validateAuthData : ( ) => Promise . resolve ( ) ,
} ;
const payload = { authData : { id : 'user1' , token : 'fakeToken' } } ;
const payload2 = { authData : { id : 'user1' , token : 'fakeToken2' } } ;
await reconfigureServer ( { auth : { fakeAuthProvider } } ) ;
const user = await Parse . User . logInWith ( 'fakeAuthProvider' , payload ) ;
const user2 = await Parse . User . logInWith ( 'fakeAuthProvider' , payload , {
sessionToken : user . getSessionToken ( ) ,
} ) ;
const user3 = await Parse . User . logInWith ( 'fakeAuthProvider' , payload2 , {
sessionToken : user2 . getSessionToken ( ) ,
} ) ;
expect ( user . id ) . toEqual ( user2 . id ) ;
expect ( user . id ) . toEqual ( user3 . id ) ;
} ) ;
it ( 'should support sync/async validateAppId' , async ( ) => {
const syncProvider = {
validateAppId : ( ) => true ,
appIds : 'test' ,
validateAuthData : ( ) => Promise . resolve ( ) ,
} ;
const asyncProvider = {
appIds : 'test' ,
validateAppId : ( ) => Promise . resolve ( true ) ,
validateAuthData : ( ) => Promise . resolve ( ) ,
} ;
const payload = { authData : { id : 'user1' , token : 'fakeToken' } } ;
const syncSpy = spyOn ( syncProvider , 'validateAppId' ) ;
const asyncSpy = spyOn ( asyncProvider , 'validateAppId' ) ;
await reconfigureServer ( { auth : { asyncProvider , syncProvider } } ) ;
const user = await Parse . User . logInWith ( 'asyncProvider' , payload ) ;
const user2 = await Parse . User . logInWith ( 'syncProvider' , payload ) ;
expect ( user . getSessionToken ( ) ) . toBeDefined ( ) ;
expect ( user2 . getSessionToken ( ) ) . toBeDefined ( ) ;
expect ( syncSpy ) . toHaveBeenCalledTimes ( 1 ) ;
expect ( asyncSpy ) . toHaveBeenCalledTimes ( 1 ) ;
} ) ;
2018-09-01 13:58:06 -04:00
it ( 'unlink and link with custom provider' , async ( ) => {
2018-02-17 09:55:30 -05:00
const provider = getMockMyOauthProvider ( ) ;
2016-02-04 14:03:39 -05:00
Parse . User . _registerAuthenticationProvider ( provider ) ;
2018-09-01 13:58:06 -04:00
const model = await Parse . User . _logInWith ( 'myoauth' ) ;
ok ( model instanceof Parse . User , 'Model should be a Parse.User' ) ;
2018-08-05 13:58:07 -04:00
strictEqual ( Parse . User . current ( ) , model ) ;
2018-09-01 13:58:06 -04:00
ok ( model . extended ( ) , 'Should have used the subclass.' ) ;
2018-08-05 13:58:07 -04:00
strictEqual ( provider . authData . id , provider . synchronizedUserId ) ;
strictEqual ( provider . authData . access _token , provider . synchronizedAuthToken ) ;
2020-10-25 15:06:58 -05:00
strictEqual ( provider . authData . expiration _date , provider . synchronizedExpiration ) ;
2018-09-01 13:58:06 -04:00
ok ( model . _isLinked ( 'myoauth' ) , 'User should be linked to myoauth' ) ;
await model . _unlinkFrom ( 'myoauth' ) ;
ok ( ! model . _isLinked ( 'myoauth' ) , 'User should not be linked to myoauth' ) ;
ok ( ! provider . synchronizedUserId , 'User id should be cleared' ) ;
ok ( ! provider . synchronizedAuthToken , 'Auth token should be cleared' ) ;
ok ( ! provider . synchronizedExpiration , 'Expiration should be cleared' ) ;
2018-08-05 13:58:07 -04:00
// make sure the auth data is properly deleted
const config = Config . get ( Parse . applicationId ) ;
2018-09-01 13:58:06 -04:00
const res = await config . database . adapter . find (
'_User' ,
{
2020-10-25 15:06:58 -05:00
fields : Object . assign ( { } , defaultColumns . _Default , defaultColumns . _Installation ) ,
2018-09-01 13:58:06 -04:00
} ,
{ objectId : model . id } ,
{ }
) ;
2018-08-05 13:58:07 -04:00
expect ( res . length ) . toBe ( 1 ) ;
expect ( res [ 0 ] . _auth _data _myoauth ) . toBeUndefined ( ) ;
expect ( res [ 0 ] . _auth _data _myoauth ) . not . toBeNull ( ) ;
2018-09-01 13:58:06 -04:00
await model . _linkWith ( 'myoauth' ) ;
2018-08-05 13:58:07 -04:00
2018-09-01 13:58:06 -04:00
ok ( provider . synchronizedUserId , 'User id should have a value' ) ;
ok ( provider . synchronizedAuthToken , 'Auth token should have a value' ) ;
ok ( provider . synchronizedExpiration , 'Expiration should have a value' ) ;
ok ( model . _isLinked ( 'myoauth' ) , 'User should be linked to myoauth' ) ;
2016-02-04 14:03:39 -05:00
} ) ;
2016-12-06 17:09:43 -05:00
function validateValidator ( validator ) {
expect ( typeof validator ) . toBe ( 'function' ) ;
}
2017-08-29 11:20:51 -07:00
function validateAuthenticationHandler ( authenticationHandler ) {
expect ( authenticationHandler ) . not . toBeUndefined ( ) ;
2020-10-25 15:06:58 -05:00
expect ( typeof authenticationHandler . getValidatorForProvider ) . toBe ( 'function' ) ;
expect ( typeof authenticationHandler . getValidatorForProvider ) . toBe ( 'function' ) ;
2016-12-06 17:09:43 -05:00
}
2016-12-08 11:54:06 -05:00
function validateAuthenticationAdapter ( authAdapter ) {
expect ( authAdapter ) . not . toBeUndefined ( ) ;
2018-09-01 13:58:06 -04:00
if ( ! authAdapter ) {
return ;
}
2016-12-08 11:54:06 -05:00
expect ( typeof authAdapter . validateAuthData ) . toBe ( 'function' ) ;
expect ( typeof authAdapter . validateAppId ) . toBe ( 'function' ) ;
}
2020-08-20 12:09:54 -05:00
it ( 'properly loads custom adapter' , done => {
2018-02-17 09:55:30 -05:00
const validAuthData = {
2016-12-06 17:09:43 -05:00
id : 'hello' ,
2018-09-01 13:58:06 -04:00
token : 'world' ,
} ;
2016-12-07 15:17:05 -08:00
const adapter = {
2020-07-15 18:56:08 +02:00
validateAppId : function ( ) {
2016-12-06 17:09:43 -05:00
return Promise . resolve ( ) ;
} ,
2020-07-15 18:56:08 +02:00
validateAuthData : function ( authData ) {
2020-10-25 15:06:58 -05:00
if ( authData . id == validAuthData . id && authData . token == validAuthData . token ) {
2016-12-06 17:09:43 -05:00
return Promise . resolve ( ) ;
}
return Promise . reject ( ) ;
2018-09-01 13:58:06 -04:00
} ,
2016-12-06 17:09:43 -05:00
} ;
2016-02-04 14:03:39 -05:00
2016-12-07 15:17:05 -08:00
const authDataSpy = spyOn ( adapter , 'validateAuthData' ) . and . callThrough ( ) ;
const appIdSpy = spyOn ( adapter , 'validateAppId' ) . and . callThrough ( ) ;
2016-12-06 17:09:43 -05:00
2016-12-07 15:17:05 -08:00
const authenticationHandler = authenticationLoader ( {
2018-09-01 13:58:06 -04:00
customAuthentication : adapter ,
2016-12-06 17:09:43 -05:00
} ) ;
validateAuthenticationHandler ( authenticationHandler ) ;
2022-11-11 03:35:39 +11:00
const { validator } = authenticationHandler . getValidatorForProvider ( 'customAuthentication' ) ;
2016-12-06 17:09:43 -05:00
validateValidator ( validator ) ;
2022-11-11 03:35:39 +11:00
validator ( validAuthData , { } , { } ) . then (
2018-09-01 13:58:06 -04:00
( ) => {
expect ( authDataSpy ) . toHaveBeenCalled ( ) ;
// AppIds are not provided in the adapter, should not be called
expect ( appIdSpy ) . not . toHaveBeenCalled ( ) ;
done ( ) ;
} ,
2020-08-20 12:09:54 -05:00
err => {
2018-09-01 13:58:06 -04:00
jfail ( err ) ;
done ( ) ;
}
) ;
2016-12-06 17:09:43 -05:00
} ) ;
2020-08-20 12:09:54 -05:00
it ( 'properly loads custom adapter module object' , done => {
2016-12-07 15:17:05 -08:00
const authenticationHandler = authenticationLoader ( {
2018-09-01 13:58:06 -04:00
customAuthentication : path . resolve ( './spec/support/CustomAuth.js' ) ,
2016-12-06 17:09:43 -05:00
} ) ;
validateAuthenticationHandler ( authenticationHandler ) ;
2022-11-11 03:35:39 +11:00
const { validator } = authenticationHandler . getValidatorForProvider ( 'customAuthentication' ) ;
2016-12-06 17:09:43 -05:00
validateValidator ( validator ) ;
2022-11-11 03:35:39 +11:00
validator (
{
token : 'my-token' ,
} ,
{ } ,
{ }
) . then (
2018-09-01 13:58:06 -04:00
( ) => {
done ( ) ;
} ,
2020-08-20 12:09:54 -05:00
err => {
2018-09-01 13:58:06 -04:00
jfail ( err ) ;
done ( ) ;
}
) ;
2016-12-06 17:09:43 -05:00
} ) ;
2020-08-20 12:09:54 -05:00
it ( 'properly loads custom adapter module object (again)' , done => {
2016-12-07 15:17:05 -08:00
const authenticationHandler = authenticationLoader ( {
2018-09-01 13:58:06 -04:00
customAuthentication : {
module : path . resolve ( './spec/support/CustomAuthFunction.js' ) ,
options : { token : 'valid-token' } ,
} ,
2016-12-06 17:09:43 -05:00
} ) ;
validateAuthenticationHandler ( authenticationHandler ) ;
2022-11-11 03:35:39 +11:00
const { validator } = authenticationHandler . getValidatorForProvider ( 'customAuthentication' ) ;
2016-12-06 17:09:43 -05:00
validateValidator ( validator ) ;
2022-11-11 03:35:39 +11:00
validator (
{
token : 'valid-token' ,
} ,
{ } ,
{ }
) . then (
2018-09-01 13:58:06 -04:00
( ) => {
done ( ) ;
} ,
2020-08-20 12:09:54 -05:00
err => {
2018-09-01 13:58:06 -04:00
jfail ( err ) ;
done ( ) ;
}
) ;
2016-12-06 17:09:43 -05:00
} ) ;
2016-12-08 11:54:06 -05:00
it ( 'properly loads a default adapter with options' , ( ) => {
const options = {
facebook : {
2018-09-01 13:58:06 -04:00
appIds : [ 'a' , 'b' ] ,
2019-06-20 14:15:57 -05:00
appSecret : 'secret' ,
2018-09-01 13:58:06 -04:00
} ,
2016-12-08 11:54:06 -05:00
} ;
2020-10-25 15:06:58 -05:00
const { adapter , appIds , providerOptions } = authenticationLoader . loadAuthAdapter (
'facebook' ,
options
) ;
2016-12-08 11:54:06 -05:00
validateAuthenticationAdapter ( adapter ) ;
expect ( appIds ) . toEqual ( [ 'a' , 'b' ] ) ;
expect ( providerOptions ) . toEqual ( options . facebook ) ;
} ) ;
2019-06-20 14:15:57 -05:00
it ( 'should handle Facebook appSecret for validating appIds' , async ( ) => {
const httpsRequest = require ( '../lib/Adapters/Auth/httpsRequest' ) ;
spyOn ( httpsRequest , 'get' ) . and . callFake ( ( ) => {
return Promise . resolve ( { id : 'a' } ) ;
} ) ;
const options = {
facebook : {
appIds : [ 'a' , 'b' ] ,
appSecret : 'secret_sauce' ,
} ,
} ;
const authData = {
access _token : 'badtoken' ,
} ;
2020-10-25 15:06:58 -05:00
const { adapter , appIds , providerOptions } = authenticationLoader . loadAuthAdapter (
'facebook' ,
options
) ;
2019-06-20 14:15:57 -05:00
await adapter . validateAppId ( appIds , authData , providerOptions ) ;
2020-10-25 15:06:58 -05:00
expect ( httpsRequest . get . calls . first ( ) . args [ 0 ] . includes ( 'appsecret_proof' ) ) . toBe ( true ) ;
2019-06-20 14:15:57 -05:00
} ) ;
2022-09-20 23:03:21 +02:00
it ( 'should throw error when Facebook request appId is wrong data type' , async ( ) => {
const httpsRequest = require ( '../lib/Adapters/Auth/httpsRequest' ) ;
spyOn ( httpsRequest , 'get' ) . and . callFake ( ( ) => {
return Promise . resolve ( { id : 'a' } ) ;
} ) ;
const options = {
facebook : {
appIds : 'abcd' ,
appSecret : 'secret_sauce' ,
} ,
} ;
const authData = {
access _token : 'badtoken' ,
} ;
const { adapter , appIds , providerOptions } = authenticationLoader . loadAuthAdapter (
'facebook' ,
options
) ;
await expectAsync ( adapter . validateAppId ( appIds , authData , providerOptions ) ) . toBeRejectedWith (
new Parse . Error ( Parse . Error . OBJECT _NOT _FOUND , 'appIds must be an array.' )
) ;
} ) ;
2019-06-20 14:15:57 -05:00
it ( 'should handle Facebook appSecret for validating auth data' , async ( ) => {
const httpsRequest = require ( '../lib/Adapters/Auth/httpsRequest' ) ;
spyOn ( httpsRequest , 'get' ) . and . callFake ( ( ) => {
return Promise . resolve ( ) ;
} ) ;
const options = {
facebook : {
appIds : [ 'a' , 'b' ] ,
appSecret : 'secret_sauce' ,
} ,
} ;
const authData = {
id : 'test' ,
access _token : 'test' ,
} ;
2020-10-25 15:06:58 -05:00
const { adapter , providerOptions } = authenticationLoader . loadAuthAdapter ( 'facebook' , options ) ;
2019-06-20 14:15:57 -05:00
await adapter . validateAuthData ( authData , providerOptions ) ;
2020-10-25 15:06:58 -05:00
expect ( httpsRequest . get . calls . first ( ) . args [ 0 ] . includes ( 'appsecret_proof' ) ) . toBe ( true ) ;
2019-06-20 14:15:57 -05:00
} ) ;
2016-12-08 11:54:06 -05:00
it ( 'properly loads a custom adapter with options' , ( ) => {
const options = {
custom : {
validateAppId : ( ) => { } ,
validateAuthData : ( ) => { } ,
2018-09-01 13:58:06 -04:00
appIds : [ 'a' , 'b' ] ,
} ,
2016-12-08 11:54:06 -05:00
} ;
2020-10-25 15:06:58 -05:00
const { adapter , appIds , providerOptions } = authenticationLoader . loadAuthAdapter (
'custom' ,
options
) ;
2016-12-08 11:54:06 -05:00
validateAuthenticationAdapter ( adapter ) ;
expect ( appIds ) . toEqual ( [ 'a' , 'b' ] ) ;
expect ( providerOptions ) . toEqual ( options . custom ) ;
} ) ;
2022-05-29 09:50:43 +10:00
it ( 'can disable provider' , async ( ) => {
await reconfigureServer ( {
auth : {
myoauth : {
enabled : false ,
module : path . resolve ( _ _dirname , 'support/myoauth' ) , // relative path as it's run from src
} ,
} ,
} ) ;
const provider = getMockMyOauthProvider ( ) ;
Parse . User . _registerAuthenticationProvider ( provider ) ;
await expectAsync ( Parse . User . _logInWith ( 'myoauth' ) ) . toBeRejectedWith (
new Parse . Error ( Parse . Error . UNSUPPORTED _SERVICE , 'This authentication method is unsupported.' )
) ;
} ) ;
2016-12-06 17:09:43 -05:00
} ) ;
2018-08-12 11:05:28 -04:00
describe ( 'google auth adapter' , ( ) => {
const google = require ( '../lib/Adapters/Auth/google' ) ;
2020-07-15 18:56:08 +02:00
const jwt = require ( 'jsonwebtoken' ) ;
2023-02-07 22:45:30 +11:00
const authUtils = require ( '../lib/Adapters/Auth/utils' ) ;
2018-08-12 11:05:28 -04:00
2020-07-15 18:56:08 +02:00
it ( 'should throw error with missing id_token' , async ( ) => {
try {
await google . validateAuthData ( { } , { } ) ;
fail ( ) ;
} catch ( e ) {
expect ( e . message ) . toBe ( 'id token is invalid for this user.' ) ;
}
2018-08-12 11:05:28 -04:00
} ) ;
2020-07-15 18:56:08 +02:00
it ( 'should not decode invalid id_token' , async ( ) => {
try {
2020-10-25 15:06:58 -05:00
await google . validateAuthData ( { id : 'the_user_id' , id _token : 'the_token' } , { } ) ;
2020-07-15 18:56:08 +02:00
fail ( ) ;
} catch ( e ) {
expect ( e . message ) . toBe ( 'provided token does not decode as JWT' ) ;
}
2018-08-12 11:05:28 -04:00
} ) ;
2020-07-15 18:56:08 +02:00
// it('should throw error if public key used to encode token is not available', async () => {
// const fakeDecodedToken = { header: { kid: '789', alg: 'RS256' } };
// try {
2023-02-07 22:45:30 +11:00
// spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken);
2020-07-15 18:56:08 +02:00
// await google.validateAuthData({ id: 'the_user_id', id_token: 'the_token' }, {});
// fail();
// } catch (e) {
// expect(e.message).toBe(
// `Unable to find matching key for Key ID: ${fakeDecodedToken.header.kid}`
// );
// }
// });
2024-07-18 15:41:04 +02:00
it ( '(using client id as string) should verify id_token (google.com)' , async ( ) => {
2020-07-15 18:56:08 +02:00
const fakeClaim = {
iss : 'https://accounts.google.com' ,
aud : 'secret' ,
exp : Date . now ( ) ,
sub : 'the_user_id' ,
} ;
const fakeDecodedToken = { header : { kid : '123' , alg : 'RS256' } } ;
2023-02-07 22:45:30 +11:00
spyOn ( authUtils , 'getHeaderFromToken' ) . and . callFake ( ( ) => fakeDecodedToken ) ;
2020-07-15 18:56:08 +02:00
spyOn ( jwt , 'verify' ) . and . callFake ( ( ) => fakeClaim ) ;
const result = await google . validateAuthData (
{ id : 'the_user_id' , id _token : 'the_token' } ,
{ clientId : 'secret' }
2018-09-01 13:58:06 -04:00
) ;
2020-07-15 18:56:08 +02:00
expect ( result ) . toEqual ( fakeClaim ) ;
2018-08-12 11:05:28 -04:00
} ) ;
2024-07-18 15:41:04 +02:00
it ( '(using client id as string) should throw error with with invalid jwt issuer (google.com)' , async ( ) => {
2020-07-15 18:56:08 +02:00
const fakeClaim = {
iss : 'https://not.google.com' ,
sub : 'the_user_id' ,
} ;
const fakeDecodedToken = { header : { kid : '123' , alg : 'RS256' } } ;
2023-02-07 22:45:30 +11:00
spyOn ( authUtils , 'getHeaderFromToken' ) . and . callFake ( ( ) => fakeDecodedToken ) ;
2020-07-15 18:56:08 +02:00
spyOn ( jwt , 'verify' ) . and . callFake ( ( ) => fakeClaim ) ;
try {
await google . validateAuthData (
{ id : 'the_user_id' , id _token : 'the_token' } ,
{ clientId : 'secret' }
) ;
fail ( ) ;
} catch ( e ) {
expect ( e . message ) . toBe (
2020-07-29 20:25:59 +05:30
'id token not issued by correct provider - expected: accounts.google.com or https://accounts.google.com | from: https://not.google.com'
2020-07-15 18:56:08 +02:00
) ;
}
2018-08-12 11:05:28 -04:00
} ) ;
2020-07-15 18:56:08 +02:00
xit ( '(using client id as string) should throw error with invalid jwt client_id' , async ( ) => {
const fakeClaim = {
iss : 'https://accounts.google.com' ,
aud : 'secret' ,
exp : Date . now ( ) ,
sub : 'the_user_id' ,
} ;
const fakeDecodedToken = { header : { kid : '123' , alg : 'RS256' } } ;
2023-02-07 22:45:30 +11:00
spyOn ( authUtils , 'getHeaderFromToken' ) . and . callFake ( ( ) => fakeDecodedToken ) ;
2020-07-15 18:56:08 +02:00
spyOn ( jwt , 'verify' ) . and . callFake ( ( ) => fakeClaim ) ;
2018-08-12 11:05:28 -04:00
try {
2018-09-01 13:58:06 -04:00
await google . validateAuthData (
2020-07-15 18:56:08 +02:00
{ id : 'INSERT ID HERE' , token : 'INSERT APPLE TOKEN HERE' } ,
{ clientId : 'secret' }
2018-09-01 13:58:06 -04:00
) ;
fail ( ) ;
} catch ( e ) {
2020-07-15 18:56:08 +02:00
expect ( e . message ) . toBe ( 'jwt audience invalid. expected: secret' ) ;
2018-08-12 11:05:28 -04:00
}
} ) ;
2020-07-15 18:56:08 +02:00
xit ( 'should throw error with invalid user id' , async ( ) => {
const fakeClaim = {
iss : 'https://accounts.google.com' ,
aud : 'secret' ,
exp : Date . now ( ) ,
sub : 'the_user_id' ,
} ;
const fakeDecodedToken = { header : { kid : '123' , alg : 'RS256' } } ;
2023-02-07 22:45:30 +11:00
spyOn ( authUtils , 'getHeaderFromToken' ) . and . callFake ( ( ) => fakeDecodedToken ) ;
2020-07-15 18:56:08 +02:00
spyOn ( jwt , 'verify' ) . and . callFake ( ( ) => fakeClaim ) ;
2018-08-12 11:05:28 -04:00
try {
2018-09-01 13:58:06 -04:00
await google . validateAuthData (
2020-07-15 18:56:08 +02:00
{ id : 'invalid user' , token : 'INSERT APPLE TOKEN HERE' } ,
{ clientId : 'INSERT CLIENT ID HERE' }
2018-09-01 13:58:06 -04:00
) ;
fail ( ) ;
} catch ( e ) {
2020-07-15 18:56:08 +02:00
expect ( e . message ) . toBe ( 'auth data is invalid for this user.' ) ;
2018-08-12 11:05:28 -04:00
}
} ) ;
} ) ;
2019-04-11 18:05:55 +02:00
2020-08-31 08:11:07 +02:00
describe ( 'keycloak auth adapter' , ( ) => {
const keycloak = require ( '../lib/Adapters/Auth/keycloak' ) ;
const httpsRequest = require ( '../lib/Adapters/Auth/httpsRequest' ) ;
it ( 'validateAuthData should fail without access token' , async ( ) => {
const authData = {
id : 'fakeid' ,
} ;
try {
await keycloak . validateAuthData ( authData ) ;
fail ( ) ;
} catch ( e ) {
expect ( e . message ) . toBe ( 'Missing access token and/or User id' ) ;
}
} ) ;
it ( 'validateAuthData should fail without user id' , async ( ) => {
const authData = {
access _token : 'sometoken' ,
} ;
try {
await keycloak . validateAuthData ( authData ) ;
fail ( ) ;
} catch ( e ) {
expect ( e . message ) . toBe ( 'Missing access token and/or User id' ) ;
}
} ) ;
it ( 'validateAuthData should fail without config' , async ( ) => {
const options = {
keycloak : {
config : null ,
} ,
} ;
const authData = {
id : 'fakeid' ,
access _token : 'sometoken' ,
} ;
2020-10-25 15:06:58 -05:00
const { adapter , providerOptions } = authenticationLoader . loadAuthAdapter ( 'keycloak' , options ) ;
2020-08-31 08:11:07 +02:00
try {
await adapter . validateAuthData ( authData , providerOptions ) ;
fail ( ) ;
} catch ( e ) {
expect ( e . message ) . toBe ( 'Missing keycloak configuration' ) ;
}
} ) ;
it ( 'validateAuthData should fail connect error' , async ( ) => {
spyOn ( httpsRequest , 'get' ) . and . callFake ( ( ) => {
return Promise . reject ( {
text : JSON . stringify ( { error : 'hosting_error' } ) ,
} ) ;
} ) ;
const options = {
keycloak : {
config : {
'auth-server-url' : 'http://example.com' ,
realm : 'new' ,
} ,
} ,
} ;
const authData = {
id : 'fakeid' ,
access _token : 'sometoken' ,
} ;
2020-10-25 15:06:58 -05:00
const { adapter , providerOptions } = authenticationLoader . loadAuthAdapter ( 'keycloak' , options ) ;
2020-08-31 08:11:07 +02:00
try {
await adapter . validateAuthData ( authData , providerOptions ) ;
fail ( ) ;
} catch ( e ) {
expect ( e . message ) . toBe ( 'Could not connect to the authentication server' ) ;
}
} ) ;
it ( 'validateAuthData should fail with error description' , async ( ) => {
spyOn ( httpsRequest , 'get' ) . and . callFake ( ( ) => {
return Promise . reject ( {
text : JSON . stringify ( { error _description : 'custom error message' } ) ,
} ) ;
} ) ;
const options = {
keycloak : {
config : {
'auth-server-url' : 'http://example.com' ,
realm : 'new' ,
} ,
} ,
} ;
const authData = {
id : 'fakeid' ,
access _token : 'sometoken' ,
} ;
2020-10-25 15:06:58 -05:00
const { adapter , providerOptions } = authenticationLoader . loadAuthAdapter ( 'keycloak' , options ) ;
2020-08-31 08:11:07 +02:00
try {
await adapter . validateAuthData ( authData , providerOptions ) ;
fail ( ) ;
} catch ( e ) {
expect ( e . message ) . toBe ( 'custom error message' ) ;
}
} ) ;
it ( 'validateAuthData should fail with invalid auth' , async ( ) => {
spyOn ( httpsRequest , 'get' ) . and . callFake ( ( ) => {
return Promise . resolve ( { } ) ;
} ) ;
const options = {
keycloak : {
config : {
'auth-server-url' : 'http://example.com' ,
realm : 'new' ,
} ,
} ,
} ;
const authData = {
id : 'fakeid' ,
access _token : 'sometoken' ,
} ;
2020-10-25 15:06:58 -05:00
const { adapter , providerOptions } = authenticationLoader . loadAuthAdapter ( 'keycloak' , options ) ;
2020-08-31 08:11:07 +02:00
try {
await adapter . validateAuthData ( authData , providerOptions ) ;
fail ( ) ;
} catch ( e ) {
expect ( e . message ) . toBe ( 'Invalid authentication' ) ;
}
} ) ;
it ( 'validateAuthData should fail with invalid groups' , async ( ) => {
spyOn ( httpsRequest , 'get' ) . and . callFake ( ( ) => {
return Promise . resolve ( {
data : {
sub : 'fakeid' ,
roles : [ 'role1' ] ,
groups : [ 'unknown' ] ,
} ,
} ) ;
} ) ;
const options = {
keycloak : {
config : {
'auth-server-url' : 'http://example.com' ,
realm : 'new' ,
} ,
} ,
} ;
const authData = {
id : 'fakeid' ,
access _token : 'sometoken' ,
roles : [ 'role1' ] ,
groups : [ 'group1' ] ,
} ;
2020-10-25 15:06:58 -05:00
const { adapter , providerOptions } = authenticationLoader . loadAuthAdapter ( 'keycloak' , options ) ;
2020-08-31 08:11:07 +02:00
try {
await adapter . validateAuthData ( authData , providerOptions ) ;
fail ( ) ;
} catch ( e ) {
expect ( e . message ) . toBe ( 'Invalid authentication' ) ;
}
} ) ;
it ( 'validateAuthData should fail with invalid roles' , async ( ) => {
spyOn ( httpsRequest , 'get' ) . and . callFake ( ( ) => {
return Promise . resolve ( {
data : {
sub : 'fakeid' ,
roles : 'unknown' ,
groups : [ 'group1' ] ,
} ,
} ) ;
} ) ;
const options = {
keycloak : {
config : {
'auth-server-url' : 'http://example.com' ,
realm : 'new' ,
} ,
} ,
} ;
const authData = {
id : 'fakeid' ,
access _token : 'sometoken' ,
roles : [ 'role1' ] ,
groups : [ 'group1' ] ,
} ;
2020-10-25 15:06:58 -05:00
const { adapter , providerOptions } = authenticationLoader . loadAuthAdapter ( 'keycloak' , options ) ;
2020-08-31 08:11:07 +02:00
try {
await adapter . validateAuthData ( authData , providerOptions ) ;
fail ( ) ;
} catch ( e ) {
expect ( e . message ) . toBe ( 'Invalid authentication' ) ;
}
} ) ;
it ( 'validateAuthData should handle authentication' , async ( ) => {
spyOn ( httpsRequest , 'get' ) . and . callFake ( ( ) => {
return Promise . resolve ( {
data : {
sub : 'fakeid' ,
roles : [ 'role1' ] ,
groups : [ 'group1' ] ,
} ,
} ) ;
} ) ;
const options = {
keycloak : {
config : {
'auth-server-url' : 'http://example.com' ,
realm : 'new' ,
} ,
} ,
} ;
const authData = {
id : 'fakeid' ,
access _token : 'sometoken' ,
roles : [ 'role1' ] ,
groups : [ 'group1' ] ,
} ;
2020-10-25 15:06:58 -05:00
const { adapter , providerOptions } = authenticationLoader . loadAuthAdapter ( 'keycloak' , options ) ;
2020-08-31 08:11:07 +02:00
await adapter . validateAuthData ( authData , providerOptions ) ;
expect ( httpsRequest . get ) . toHaveBeenCalledWith ( {
host : 'http://example.com' ,
path : '/realms/new/protocol/openid-connect/userinfo' ,
headers : {
Authorization : 'Bearer sometoken' ,
} ,
} ) ;
} ) ;
} ) ;
2019-06-19 16:05:09 -05:00
describe ( 'apple signin auth adapter' , ( ) => {
2019-07-03 16:28:29 -05:00
const apple = require ( '../lib/Adapters/Auth/apple' ) ;
2019-06-19 16:05:09 -05:00
const jwt = require ( 'jsonwebtoken' ) ;
2023-02-07 22:45:30 +11:00
const authUtils = require ( '../lib/Adapters/Auth/utils' ) ;
2019-06-19 16:05:09 -05:00
2020-03-21 17:04:10 -07:00
it ( '(using client id as string) should throw error with missing id_token' , async ( ) => {
2019-06-19 16:05:09 -05:00
try {
2020-03-11 15:29:20 -05:00
await apple . validateAuthData ( { } , { clientId : 'secret' } ) ;
2019-06-19 16:05:09 -05:00
fail ( ) ;
} catch ( e ) {
2019-07-03 16:28:29 -05:00
expect ( e . message ) . toBe ( 'id token is invalid for this user.' ) ;
2019-06-19 16:05:09 -05:00
}
} ) ;
2020-03-21 17:04:10 -07:00
it ( '(using client id as array) should throw error with missing id_token' , async ( ) => {
try {
2020-08-25 17:24:53 +02:00
await apple . validateAuthData ( { } , { clientId : [ 'secret' ] } ) ;
2020-03-21 17:04:10 -07:00
fail ( ) ;
} catch ( e ) {
expect ( e . message ) . toBe ( 'id token is invalid for this user.' ) ;
}
} ) ;
2020-03-11 15:29:20 -05:00
it ( 'should not decode invalid id_token' , async ( ) => {
try {
await apple . validateAuthData (
{ id : 'the_user_id' , token : 'the_token' } ,
{ clientId : 'secret' }
) ;
fail ( ) ;
} catch ( e ) {
expect ( e . message ) . toBe ( 'provided token does not decode as JWT' ) ;
}
} ) ;
it ( 'should throw error if public key used to encode token is not available' , async ( ) => {
const fakeDecodedToken = { header : { kid : '789' , alg : 'RS256' } } ;
try {
2023-02-07 22:45:30 +11:00
spyOn ( authUtils , 'getHeaderFromToken' ) . and . callFake ( ( ) => fakeDecodedToken . header ) ;
2020-03-11 15:29:20 -05:00
await apple . validateAuthData (
{ id : 'the_user_id' , token : 'the_token' } ,
{ clientId : 'secret' }
) ;
fail ( ) ;
} catch ( e ) {
expect ( e . message ) . toBe (
` Unable to find matching key for Key ID: ${ fakeDecodedToken . header . kid } `
) ;
}
} ) ;
2024-07-18 15:41:04 +02:00
it ( 'should use algorithm from key header to verify id_token (apple.com)' , async ( ) => {
2020-03-11 15:29:20 -05:00
const fakeClaim = {
iss : 'https://appleid.apple.com' ,
aud : 'secret' ,
exp : Date . now ( ) ,
sub : 'the_user_id' ,
} ;
const fakeDecodedToken = { header : { kid : '123' , alg : 'RS256' } } ;
2023-12-10 02:42:40 +01:00
const fakeSigningKey = { kid : '123' , rsaPublicKey : 'the_rsa_public_key' } ;
2023-02-07 22:45:30 +11:00
spyOn ( authUtils , 'getHeaderFromToken' ) . and . callFake ( ( ) => fakeDecodedToken . header ) ;
2023-12-10 02:42:40 +01:00
spyOn ( authUtils , 'getSigningKey' ) . and . resolveTo ( fakeSigningKey ) ;
2020-03-11 15:29:20 -05:00
spyOn ( jwt , 'verify' ) . and . callFake ( ( ) => fakeClaim ) ;
const result = await apple . validateAuthData (
{ id : 'the_user_id' , token : 'the_token' } ,
{ clientId : 'secret' }
) ;
expect ( result ) . toEqual ( fakeClaim ) ;
2020-10-25 15:06:58 -05:00
expect ( jwt . verify . calls . first ( ) . args [ 2 ] . algorithms ) . toEqual ( fakeDecodedToken . header . alg ) ;
2020-03-11 15:29:20 -05:00
} ) ;
2019-06-19 16:05:09 -05:00
it ( 'should not verify invalid id_token' , async ( ) => {
2020-03-11 15:29:20 -05:00
const fakeDecodedToken = { header : { kid : '123' , alg : 'RS256' } } ;
2023-12-10 02:42:40 +01:00
const fakeSigningKey = { kid : '123' , rsaPublicKey : 'the_rsa_public_key' } ;
2023-02-07 22:45:30 +11:00
spyOn ( authUtils , 'getHeaderFromToken' ) . and . callFake ( ( ) => fakeDecodedToken ) ;
2023-12-10 02:42:40 +01:00
spyOn ( authUtils , 'getSigningKey' ) . and . resolveTo ( fakeSigningKey ) ;
2020-03-11 15:29:20 -05:00
2019-06-19 16:05:09 -05:00
try {
await apple . validateAuthData (
2019-08-08 01:08:14 +02:00
{ id : 'the_user_id' , token : 'the_token' } ,
2020-03-11 15:29:20 -05:00
{ clientId : 'secret' }
2019-06-19 16:05:09 -05:00
) ;
fail ( ) ;
} catch ( e ) {
expect ( e . message ) . toBe ( 'jwt malformed' ) ;
}
} ) ;
2020-03-21 17:04:10 -07:00
it ( '(using client id as array) should not verify invalid id_token' , async ( ) => {
try {
await apple . validateAuthData (
{ id : 'the_user_id' , token : 'the_token' } ,
2020-08-25 17:24:53 +02:00
{ clientId : [ 'secret' ] }
2020-03-21 17:04:10 -07:00
) ;
fail ( ) ;
} catch ( e ) {
expect ( e . message ) . toBe ( 'provided token does not decode as JWT' ) ;
}
} ) ;
2024-07-18 15:41:04 +02:00
it ( '(using client id as string) should verify id_token (apple.com)' , async ( ) => {
2019-06-19 16:05:09 -05:00
const fakeClaim = {
iss : 'https://appleid.apple.com' ,
aud : 'secret' ,
exp : Date . now ( ) ,
2019-08-08 01:08:14 +02:00
sub : 'the_user_id' ,
2019-06-19 16:05:09 -05:00
} ;
2020-03-11 15:29:20 -05:00
const fakeDecodedToken = { header : { kid : '123' , alg : 'RS256' } } ;
2023-12-10 02:42:40 +01:00
const fakeSigningKey = { kid : '123' , rsaPublicKey : 'the_rsa_public_key' } ;
2023-02-07 22:45:30 +11:00
spyOn ( authUtils , 'getHeaderFromToken' ) . and . callFake ( ( ) => fakeDecodedToken ) ;
2023-12-10 02:42:40 +01:00
spyOn ( authUtils , 'getSigningKey' ) . and . resolveTo ( fakeSigningKey ) ;
2019-06-19 16:05:09 -05:00
spyOn ( jwt , 'verify' ) . and . callFake ( ( ) => fakeClaim ) ;
const result = await apple . validateAuthData (
2019-08-08 01:08:14 +02:00
{ id : 'the_user_id' , token : 'the_token' } ,
2020-03-11 15:29:20 -05:00
{ clientId : 'secret' }
2019-06-19 16:05:09 -05:00
) ;
expect ( result ) . toEqual ( fakeClaim ) ;
} ) ;
2024-07-18 15:41:04 +02:00
it ( '(using client id as array) should verify id_token (apple.com)' , async ( ) => {
2020-03-21 17:04:10 -07:00
const fakeClaim = {
iss : 'https://appleid.apple.com' ,
aud : 'secret' ,
exp : Date . now ( ) ,
sub : 'the_user_id' ,
} ;
const fakeDecodedToken = { header : { kid : '123' , alg : 'RS256' } } ;
2023-12-10 02:42:40 +01:00
const fakeSigningKey = { kid : '123' , rsaPublicKey : 'the_rsa_public_key' } ;
2023-02-07 22:45:30 +11:00
spyOn ( authUtils , 'getHeaderFromToken' ) . and . callFake ( ( ) => fakeDecodedToken ) ;
2023-12-10 02:42:40 +01:00
spyOn ( authUtils , 'getSigningKey' ) . and . resolveTo ( fakeSigningKey ) ;
2020-03-21 17:04:10 -07:00
spyOn ( jwt , 'verify' ) . and . callFake ( ( ) => fakeClaim ) ;
const result = await apple . validateAuthData (
{ id : 'the_user_id' , token : 'the_token' } ,
{ clientId : [ 'secret' ] }
) ;
expect ( result ) . toEqual ( fakeClaim ) ;
} ) ;
2024-07-18 15:41:04 +02:00
it ( '(using client id as array with multiple items) should verify id_token (apple.com)' , async ( ) => {
2020-03-21 17:04:10 -07:00
const fakeClaim = {
iss : 'https://appleid.apple.com' ,
aud : 'secret' ,
exp : Date . now ( ) ,
sub : 'the_user_id' ,
} ;
const fakeDecodedToken = { header : { kid : '123' , alg : 'RS256' } } ;
2023-12-10 02:42:40 +01:00
const fakeSigningKey = { kid : '123' , rsaPublicKey : 'the_rsa_public_key' } ;
2023-02-07 22:45:30 +11:00
spyOn ( authUtils , 'getHeaderFromToken' ) . and . callFake ( ( ) => fakeDecodedToken ) ;
2023-12-10 02:42:40 +01:00
spyOn ( authUtils , 'getSigningKey' ) . and . resolveTo ( fakeSigningKey ) ;
2020-03-21 17:04:10 -07:00
spyOn ( jwt , 'verify' ) . and . callFake ( ( ) => fakeClaim ) ;
const result = await apple . validateAuthData (
{ id : 'the_user_id' , token : 'the_token' } ,
{ clientId : [ 'secret' , 'secret 123' ] }
) ;
expect ( result ) . toEqual ( fakeClaim ) ;
} ) ;
2024-07-18 15:41:04 +02:00
it ( '(using client id as string) should throw error with with invalid jwt issuer (apple.com)' , async ( ) => {
2019-06-19 16:05:09 -05:00
const fakeClaim = {
iss : 'https://not.apple.com' ,
2019-08-08 01:08:14 +02:00
sub : 'the_user_id' ,
2019-06-19 16:05:09 -05:00
} ;
2020-03-11 15:29:20 -05:00
const fakeDecodedToken = { header : { kid : '123' , alg : 'RS256' } } ;
2023-12-10 02:42:40 +01:00
const fakeSigningKey = { kid : '123' , rsaPublicKey : 'the_rsa_public_key' } ;
2023-02-07 22:45:30 +11:00
spyOn ( authUtils , 'getHeaderFromToken' ) . and . callFake ( ( ) => fakeDecodedToken ) ;
2023-12-10 02:42:40 +01:00
spyOn ( authUtils , 'getSigningKey' ) . and . resolveTo ( fakeSigningKey ) ;
2019-06-19 16:05:09 -05:00
spyOn ( jwt , 'verify' ) . and . callFake ( ( ) => fakeClaim ) ;
try {
await apple . validateAuthData (
2019-08-08 01:08:14 +02:00
{ id : 'the_user_id' , token : 'the_token' } ,
2020-03-11 15:29:20 -05:00
{ clientId : 'secret' }
2019-06-19 16:05:09 -05:00
) ;
fail ( ) ;
} catch ( e ) {
expect ( e . message ) . toBe (
2019-07-03 16:28:29 -05:00
'id token not issued by correct OpenID provider - expected: https://appleid.apple.com | from: https://not.apple.com'
2019-06-19 16:05:09 -05:00
) ;
}
} ) ;
2020-03-21 17:04:10 -07:00
// TODO: figure out a way to generate our own apple signed tokens, perhaps with a parse apple account
// and a private key
xit ( '(using client id as array) should throw error with with invalid jwt issuer' , async ( ) => {
2019-06-19 16:05:09 -05:00
const fakeClaim = {
2020-03-21 17:04:10 -07:00
iss : 'https://not.apple.com' ,
2019-08-08 01:08:14 +02:00
sub : 'the_user_id' ,
2019-06-19 16:05:09 -05:00
} ;
2020-03-11 15:29:20 -05:00
const fakeDecodedToken = { header : { kid : '123' , alg : 'RS256' } } ;
2023-12-10 02:42:40 +01:00
const fakeSigningKey = { kid : '123' , rsaPublicKey : 'the_rsa_public_key' } ;
2023-02-07 22:45:30 +11:00
spyOn ( authUtils , 'getHeaderFromToken' ) . and . callFake ( ( ) => fakeDecodedToken ) ;
2023-12-10 02:42:40 +01:00
spyOn ( authUtils , 'getSigningKey' ) . and . resolveTo ( fakeSigningKey ) ;
2019-06-19 16:05:09 -05:00
spyOn ( jwt , 'verify' ) . and . callFake ( ( ) => fakeClaim ) ;
try {
await apple . validateAuthData (
2020-03-21 17:04:10 -07:00
{
id : 'INSERT ID HERE' ,
token : 'INSERT APPLE TOKEN HERE WITH INVALID JWT ISSUER' ,
} ,
{ clientId : [ 'INSERT CLIENT ID HERE' ] }
2019-06-19 16:05:09 -05:00
) ;
fail ( ) ;
} catch ( e ) {
expect ( e . message ) . toBe (
2020-03-21 17:04:10 -07:00
'id token not issued by correct OpenID provider - expected: https://appleid.apple.com | from: https://not.apple.com'
2019-06-19 16:05:09 -05:00
) ;
}
} ) ;
2019-07-29 07:58:43 +02:00
2024-07-18 15:41:04 +02:00
it ( '(using client id as string) should throw error with with invalid jwt issuer with token (apple.com)' , async ( ) => {
2020-03-21 17:04:10 -07:00
const fakeClaim = {
iss : 'https://not.apple.com' ,
sub : 'the_user_id' ,
} ;
const fakeDecodedToken = { header : { kid : '123' , alg : 'RS256' } } ;
2023-12-10 02:42:40 +01:00
const fakeSigningKey = { kid : '123' , rsaPublicKey : 'the_rsa_public_key' } ;
2023-02-07 22:45:30 +11:00
spyOn ( authUtils , 'getHeaderFromToken' ) . and . callFake ( ( ) => fakeDecodedToken ) ;
2023-12-10 02:42:40 +01:00
spyOn ( authUtils , 'getSigningKey' ) . and . resolveTo ( fakeSigningKey ) ;
2020-03-21 17:04:10 -07:00
spyOn ( jwt , 'verify' ) . and . callFake ( ( ) => fakeClaim ) ;
try {
await apple . validateAuthData (
{
id : 'INSERT ID HERE' ,
token : 'INSERT APPLE TOKEN HERE WITH INVALID JWT ISSUER' ,
} ,
{ clientId : 'INSERT CLIENT ID HERE' }
) ;
fail ( ) ;
} catch ( e ) {
expect ( e . message ) . toBe (
'id token not issued by correct OpenID provider - expected: https://appleid.apple.com | from: https://not.apple.com'
) ;
}
} ) ;
// TODO: figure out a way to generate our own apple signed tokens, perhaps with a parse apple account
// and a private key
2020-08-25 17:24:53 +02:00
xit ( '(using client id as string) should throw error with invalid jwt clientId' , async ( ) => {
2020-03-21 17:04:10 -07:00
try {
await apple . validateAuthData (
{ id : 'INSERT ID HERE' , token : 'INSERT APPLE TOKEN HERE' } ,
{ clientId : 'secret' }
) ;
fail ( ) ;
} catch ( e ) {
expect ( e . message ) . toBe ( 'jwt audience invalid. expected: secret' ) ;
}
} ) ;
// TODO: figure out a way to generate our own apple signed tokens, perhaps with a parse apple account
// and a private key
2020-08-25 17:24:53 +02:00
xit ( '(using client id as array) should throw error with invalid jwt clientId' , async ( ) => {
2020-03-21 17:04:10 -07:00
try {
await apple . validateAuthData (
{ id : 'INSERT ID HERE' , token : 'INSERT APPLE TOKEN HERE' } ,
{ clientId : [ 'secret' ] }
) ;
fail ( ) ;
} catch ( e ) {
expect ( e . message ) . toBe ( 'jwt audience invalid. expected: secret' ) ;
}
} ) ;
// TODO: figure out a way to generate our own apple signed tokens, perhaps with a parse apple account
// and a private key
xit ( 'should throw error with invalid user id' , async ( ) => {
try {
await apple . validateAuthData (
{ id : 'invalid user' , token : 'INSERT APPLE TOKEN HERE' } ,
{ clientId : 'INSERT CLIENT ID HERE' }
) ;
fail ( ) ;
} catch ( e ) {
expect ( e . message ) . toBe ( 'auth data is invalid for this user.' ) ;
}
} ) ;
2024-07-18 15:41:04 +02:00
it ( 'should throw error with with invalid user id (apple.com)' , async ( ) => {
2020-03-11 15:29:20 -05:00
const fakeClaim = {
iss : 'https://appleid.apple.com' ,
aud : 'invalid_client_id' ,
sub : 'a_different_user_id' ,
} ;
const fakeDecodedToken = { header : { kid : '123' , alg : 'RS256' } } ;
2023-12-10 02:42:40 +01:00
const fakeSigningKey = { kid : '123' , rsaPublicKey : 'the_rsa_public_key' } ;
2023-02-07 22:45:30 +11:00
spyOn ( authUtils , 'getHeaderFromToken' ) . and . callFake ( ( ) => fakeDecodedToken ) ;
2023-12-10 02:42:40 +01:00
spyOn ( authUtils , 'getSigningKey' ) . and . resolveTo ( fakeSigningKey ) ;
2020-03-11 15:29:20 -05:00
spyOn ( jwt , 'verify' ) . and . callFake ( ( ) => fakeClaim ) ;
try {
await apple . validateAuthData (
{ id : 'the_user_id' , token : 'the_token' } ,
{ clientId : 'secret' }
) ;
fail ( ) ;
} catch ( e ) {
expect ( e . message ) . toBe ( 'auth data is invalid for this user.' ) ;
}
} ) ;
} ) ;
2020-03-21 17:04:10 -07:00
2019-07-29 07:58:43 +02:00
describe ( 'phant auth adapter' , ( ) => {
const httpsRequest = require ( '../lib/Adapters/Auth/httpsRequest' ) ;
it ( 'validateAuthData should throw for invalid auth' , async ( ) => {
2025-03-21 10:49:09 +01:00
await reconfigureServer ( {
auth : {
phantauth : {
enableInsecureAuth : true ,
}
}
} )
2019-07-29 07:58:43 +02:00
const authData = {
id : 'fakeid' ,
access _token : 'sometoken' ,
} ;
const { adapter } = authenticationLoader . loadAuthAdapter ( 'phantauth' , { } ) ;
2020-10-25 15:06:58 -05:00
spyOn ( httpsRequest , 'get' ) . and . callFake ( ( ) => Promise . resolve ( { sub : 'invalidID' } ) ) ;
2019-07-29 07:58:43 +02:00
try {
await adapter . validateAuthData ( authData ) ;
fail ( ) ;
} catch ( e ) {
expect ( e . message ) . toBe ( 'PhantAuth auth is invalid for this user.' ) ;
}
} ) ;
} ) ;
2019-09-27 01:23:18 +07:00
2021-02-24 14:36:24 +01:00
describe ( 'facebook limited auth adapter' , ( ) => {
const facebook = require ( '../lib/Adapters/Auth/facebook' ) ;
const jwt = require ( 'jsonwebtoken' ) ;
2023-02-07 22:45:30 +11:00
const authUtils = require ( '../lib/Adapters/Auth/utils' ) ;
2021-02-24 14:36:24 +01:00
// TODO: figure out a way to run this test alongside facebook classic tests
xit ( '(using client id as string) should throw error with missing id_token' , async ( ) => {
try {
await facebook . validateAuthData ( { } , { clientId : 'secret' } ) ;
fail ( ) ;
} catch ( e ) {
expect ( e . message ) . toBe ( 'Facebook auth is not configured.' ) ;
}
} ) ;
// TODO: figure out a way to run this test alongside facebook classic tests
xit ( '(using client id as array) should throw error with missing id_token' , async ( ) => {
try {
await facebook . validateAuthData ( { } , { clientId : [ 'secret' ] } ) ;
fail ( ) ;
} catch ( e ) {
expect ( e . message ) . toBe ( 'Facebook auth is not configured.' ) ;
}
} ) ;
it ( 'should not decode invalid id_token' , async ( ) => {
try {
await facebook . validateAuthData (
{ id : 'the_user_id' , token : 'the_token' } ,
{ clientId : 'secret' }
) ;
fail ( ) ;
} catch ( e ) {
expect ( e . message ) . toBe ( 'provided token does not decode as JWT' ) ;
}
} ) ;
it ( 'should throw error if public key used to encode token is not available' , async ( ) => {
const fakeDecodedToken = {
header : { kid : '789' , alg : 'RS256' } ,
} ;
try {
2023-02-07 22:45:30 +11:00
spyOn ( authUtils , 'getHeaderFromToken' ) . and . callFake ( ( ) => fakeDecodedToken . header ) ;
2021-02-24 14:36:24 +01:00
await facebook . validateAuthData (
{ id : 'the_user_id' , token : 'the_token' } ,
{ clientId : 'secret' }
) ;
fail ( ) ;
} catch ( e ) {
expect ( e . message ) . toBe (
` Unable to find matching key for Key ID: ${ fakeDecodedToken . header . kid } `
) ;
}
} ) ;
2024-08-13 22:13:19 +02:00
it _id ( '7bfa55ab-8fd7-4526-992e-6de3df16bf9c' ) ( it ) ( 'should use algorithm from key header to verify id_token (facebook.com)' , async ( ) => {
2021-02-24 14:36:24 +01:00
const fakeClaim = {
2024-05-16 13:54:41 +02:00
iss : 'https://www.facebook.com' ,
2021-02-24 14:36:24 +01:00
aud : 'secret' ,
exp : Date . now ( ) ,
sub : 'the_user_id' ,
} ;
2023-12-10 02:42:40 +01:00
const fakeDecodedToken = { header : { kid : '123' , alg : 'RS256' } } ;
const fakeSigningKey = { kid : '123' , rsaPublicKey : 'the_rsa_public_key' } ;
2023-02-07 22:45:30 +11:00
spyOn ( authUtils , 'getHeaderFromToken' ) . and . callFake ( ( ) => fakeDecodedToken . header ) ;
2023-12-10 02:42:40 +01:00
spyOn ( authUtils , 'getSigningKey' ) . and . resolveTo ( fakeSigningKey ) ;
2021-02-24 14:36:24 +01:00
spyOn ( jwt , 'verify' ) . and . callFake ( ( ) => fakeClaim ) ;
const result = await facebook . validateAuthData (
{ id : 'the_user_id' , token : 'the_token' } ,
{ clientId : 'secret' }
) ;
expect ( result ) . toEqual ( fakeClaim ) ;
expect ( jwt . verify . calls . first ( ) . args [ 2 ] . algorithms ) . toEqual ( fakeDecodedToken . header . alg ) ;
} ) ;
it ( 'should not verify invalid id_token' , async ( ) => {
2023-12-10 02:42:40 +01:00
const fakeDecodedToken = { header : { kid : '123' , alg : 'RS256' } } ;
const fakeSigningKey = { kid : '123' , rsaPublicKey : 'the_rsa_public_key' } ;
2023-02-07 22:45:30 +11:00
spyOn ( authUtils , 'getHeaderFromToken' ) . and . callFake ( ( ) => fakeDecodedToken ) ;
2023-12-10 02:42:40 +01:00
spyOn ( authUtils , 'getSigningKey' ) . and . resolveTo ( fakeSigningKey ) ;
2021-02-24 14:36:24 +01:00
try {
await facebook . validateAuthData (
{ id : 'the_user_id' , token : 'the_token' } ,
{ clientId : 'secret' }
) ;
fail ( ) ;
} catch ( e ) {
expect ( e . message ) . toBe ( 'jwt malformed' ) ;
}
} ) ;
it ( '(using client id as array) should not verify invalid id_token' , async ( ) => {
try {
await facebook . validateAuthData (
{ id : 'the_user_id' , token : 'the_token' } ,
{ clientId : [ 'secret' ] }
) ;
fail ( ) ;
} catch ( e ) {
expect ( e . message ) . toBe ( 'provided token does not decode as JWT' ) ;
}
} ) ;
2024-08-13 22:13:19 +02:00
it _id ( '4bcb1a1a-11f8-4e12-a3f6-73f7e25e355a' ) ( it ) ( 'using client id as string) should verify id_token (facebook.com)' , async ( ) => {
2021-02-24 14:36:24 +01:00
const fakeClaim = {
2024-05-16 13:54:41 +02:00
iss : 'https://www.facebook.com' ,
2021-02-24 14:36:24 +01:00
aud : 'secret' ,
exp : Date . now ( ) ,
sub : 'the_user_id' ,
} ;
2023-12-10 02:42:40 +01:00
const fakeDecodedToken = { header : { kid : '123' , alg : 'RS256' } } ;
const fakeSigningKey = { kid : '123' , rsaPublicKey : 'the_rsa_public_key' } ;
2023-02-07 22:45:30 +11:00
spyOn ( authUtils , 'getHeaderFromToken' ) . and . callFake ( ( ) => fakeDecodedToken ) ;
2023-12-10 02:42:40 +01:00
spyOn ( authUtils , 'getSigningKey' ) . and . resolveTo ( fakeSigningKey ) ;
2021-02-24 14:36:24 +01:00
spyOn ( jwt , 'verify' ) . and . callFake ( ( ) => fakeClaim ) ;
const result = await facebook . validateAuthData (
{ id : 'the_user_id' , token : 'the_token' } ,
{ clientId : 'secret' }
) ;
expect ( result ) . toEqual ( fakeClaim ) ;
} ) ;
2024-08-13 22:13:19 +02:00
it _id ( 'c521a272-2ac2-4d8b-b5ed-ea250336d8b1' ) ( it ) ( '(using client id as array) should verify id_token (facebook.com)' , async ( ) => {
2021-02-24 14:36:24 +01:00
const fakeClaim = {
2024-05-16 13:54:41 +02:00
iss : 'https://www.facebook.com' ,
2021-02-24 14:36:24 +01:00
aud : 'secret' ,
exp : Date . now ( ) ,
sub : 'the_user_id' ,
} ;
2023-12-10 02:42:40 +01:00
const fakeDecodedToken = { header : { kid : '123' , alg : 'RS256' } } ;
const fakeSigningKey = { kid : '123' , rsaPublicKey : 'the_rsa_public_key' } ;
2023-02-07 22:45:30 +11:00
spyOn ( authUtils , 'getHeaderFromToken' ) . and . callFake ( ( ) => fakeDecodedToken ) ;
2023-12-10 02:42:40 +01:00
spyOn ( authUtils , 'getSigningKey' ) . and . resolveTo ( fakeSigningKey ) ;
2021-02-24 14:36:24 +01:00
spyOn ( jwt , 'verify' ) . and . callFake ( ( ) => fakeClaim ) ;
const result = await facebook . validateAuthData (
{ id : 'the_user_id' , token : 'the_token' } ,
{ clientId : [ 'secret' ] }
) ;
expect ( result ) . toEqual ( fakeClaim ) ;
} ) ;
2024-08-13 22:13:19 +02:00
it _id ( 'e3f16404-18e9-4a87-a555-4710cfbdac67' ) ( it ) ( '(using client id as array with multiple items) should verify id_token (facebook.com)' , async ( ) => {
2021-02-24 14:36:24 +01:00
const fakeClaim = {
2024-05-16 13:54:41 +02:00
iss : 'https://www.facebook.com' ,
2021-02-24 14:36:24 +01:00
aud : 'secret' ,
exp : Date . now ( ) ,
sub : 'the_user_id' ,
} ;
2023-12-10 02:42:40 +01:00
const fakeDecodedToken = { header : { kid : '123' , alg : 'RS256' } } ;
const fakeSigningKey = { kid : '123' , rsaPublicKey : 'the_rsa_public_key' } ;
2023-02-07 22:45:30 +11:00
spyOn ( authUtils , 'getHeaderFromToken' ) . and . callFake ( ( ) => fakeDecodedToken ) ;
2023-12-10 02:42:40 +01:00
spyOn ( authUtils , 'getSigningKey' ) . and . resolveTo ( fakeSigningKey ) ;
2021-02-24 14:36:24 +01:00
spyOn ( jwt , 'verify' ) . and . callFake ( ( ) => fakeClaim ) ;
const result = await facebook . validateAuthData (
{ id : 'the_user_id' , token : 'the_token' } ,
{ clientId : [ 'secret' , 'secret 123' ] }
) ;
expect ( result ) . toEqual ( fakeClaim ) ;
} ) ;
2024-08-13 22:13:19 +02:00
it _id ( '549c33a1-3a6b-4732-8cf6-8f010ad4569c' ) ( it ) ( '(using client id as string) should throw error with with invalid jwt issuer (facebook.com)' , async ( ) => {
2021-02-24 14:36:24 +01:00
const fakeClaim = {
iss : 'https://not.facebook.com' ,
sub : 'the_user_id' ,
} ;
2023-12-10 02:42:40 +01:00
const fakeDecodedToken = { header : { kid : '123' , alg : 'RS256' } } ;
const fakeSigningKey = { kid : '123' , rsaPublicKey : 'the_rsa_public_key' } ;
2023-02-07 22:45:30 +11:00
spyOn ( authUtils , 'getHeaderFromToken' ) . and . callFake ( ( ) => fakeDecodedToken ) ;
2023-12-10 02:42:40 +01:00
spyOn ( authUtils , 'getSigningKey' ) . and . resolveTo ( fakeSigningKey ) ;
2021-02-24 14:36:24 +01:00
spyOn ( jwt , 'verify' ) . and . callFake ( ( ) => fakeClaim ) ;
try {
await facebook . validateAuthData (
{ id : 'the_user_id' , token : 'the_token' } ,
{ clientId : 'secret' }
) ;
fail ( ) ;
} catch ( e ) {
expect ( e . message ) . toBe (
2024-05-16 13:54:41 +02:00
'id token not issued by correct OpenID provider - expected: https://www.facebook.com | from: https://not.facebook.com'
2021-02-24 14:36:24 +01:00
) ;
}
} ) ;
// TODO: figure out a way to generate our own facebook signed tokens, perhaps with a parse facebook account
// and a private key
xit ( '(using client id as array) should throw error with with invalid jwt issuer' , async ( ) => {
const fakeClaim = {
iss : 'https://not.facebook.com' ,
sub : 'the_user_id' ,
} ;
2023-12-10 02:42:40 +01:00
const fakeDecodedToken = { header : { kid : '123' , alg : 'RS256' } } ;
const fakeSigningKey = { kid : '123' , rsaPublicKey : 'the_rsa_public_key' } ;
2023-02-07 22:45:30 +11:00
spyOn ( authUtils , 'getHeaderFromToken' ) . and . callFake ( ( ) => fakeDecodedToken ) ;
2023-12-10 02:42:40 +01:00
spyOn ( authUtils , 'getSigningKey' ) . and . resolveTo ( fakeSigningKey ) ;
2021-02-24 14:36:24 +01:00
spyOn ( jwt , 'verify' ) . and . callFake ( ( ) => fakeClaim ) ;
try {
await facebook . validateAuthData (
{
id : 'INSERT ID HERE' ,
token : 'INSERT FACEBOOK TOKEN HERE WITH INVALID JWT ISSUER' ,
} ,
{ clientId : [ 'INSERT CLIENT ID HERE' ] }
) ;
fail ( ) ;
} catch ( e ) {
expect ( e . message ) . toBe (
2024-05-16 13:54:41 +02:00
'id token not issued by correct OpenID provider - expected: https://www.facebook.com | from: https://not.facebook.com'
2021-02-24 14:36:24 +01:00
) ;
}
} ) ;
2024-07-16 03:34:10 -05:00
it ( '(using client id as string) with token' , async ( ) => {
2021-02-24 14:36:24 +01:00
const fakeClaim = {
iss : 'https://not.facebook.com' ,
sub : 'the_user_id' ,
} ;
2023-12-10 02:42:40 +01:00
const fakeDecodedToken = { header : { kid : '123' , alg : 'RS256' } } ;
const fakeSigningKey = { kid : '123' , rsaPublicKey : 'the_rsa_public_key' } ;
2023-02-07 22:45:30 +11:00
spyOn ( authUtils , 'getHeaderFromToken' ) . and . callFake ( ( ) => fakeDecodedToken ) ;
2023-12-10 02:42:40 +01:00
spyOn ( authUtils , 'getSigningKey' ) . and . resolveTo ( fakeSigningKey ) ;
2021-02-24 14:36:24 +01:00
spyOn ( jwt , 'verify' ) . and . callFake ( ( ) => fakeClaim ) ;
try {
await facebook . validateAuthData (
{
id : 'INSERT ID HERE' ,
token : 'INSERT FACEBOOK TOKEN HERE WITH INVALID JWT ISSUER' ,
} ,
{ clientId : 'INSERT CLIENT ID HERE' }
) ;
fail ( ) ;
} catch ( e ) {
expect ( e . message ) . toBe (
2024-05-16 13:54:41 +02:00
'id token not issued by correct OpenID provider - expected: https://www.facebook.com | from: https://not.facebook.com'
2021-02-24 14:36:24 +01:00
) ;
}
} ) ;
// TODO: figure out a way to generate our own facebook signed tokens, perhaps with a parse facebook account
// and a private key
xit ( '(using client id as string) should throw error with invalid jwt clientId' , async ( ) => {
try {
await facebook . validateAuthData (
{
id : 'INSERT ID HERE' ,
token : 'INSERT FACEBOOK TOKEN HERE' ,
} ,
{ clientId : 'secret' }
) ;
fail ( ) ;
} catch ( e ) {
expect ( e . message ) . toBe ( 'jwt audience invalid. expected: secret' ) ;
}
} ) ;
// TODO: figure out a way to generate our own facebook signed tokens, perhaps with a parse facebook account
// and a private key
xit ( '(using client id as array) should throw error with invalid jwt clientId' , async ( ) => {
try {
await facebook . validateAuthData (
{
id : 'INSERT ID HERE' ,
token : 'INSERT FACEBOOK TOKEN HERE' ,
} ,
{ clientId : [ 'secret' ] }
) ;
fail ( ) ;
} catch ( e ) {
expect ( e . message ) . toBe ( 'jwt audience invalid. expected: secret' ) ;
}
} ) ;
// TODO: figure out a way to generate our own facebook signed tokens, perhaps with a parse facebook account
// and a private key
xit ( 'should throw error with invalid user id' , async ( ) => {
try {
await facebook . validateAuthData (
{
id : 'invalid user' ,
token : 'INSERT FACEBOOK TOKEN HERE' ,
} ,
{ clientId : 'INSERT CLIENT ID HERE' }
) ;
fail ( ) ;
} catch ( e ) {
expect ( e . message ) . toBe ( 'auth data is invalid for this user.' ) ;
}
} ) ;
2024-08-13 22:13:19 +02:00
it _id ( 'c194d902-e697-46c9-a303-82c2d914473c' ) ( it ) ( 'should throw error with with invalid user id (facebook.com)' , async ( ) => {
2021-02-24 14:36:24 +01:00
const fakeClaim = {
2024-05-16 13:54:41 +02:00
iss : 'https://www.facebook.com' ,
2021-02-24 14:36:24 +01:00
aud : 'invalid_client_id' ,
sub : 'a_different_user_id' ,
} ;
2023-12-10 02:42:40 +01:00
const fakeDecodedToken = { header : { kid : '123' , alg : 'RS256' } } ;
const fakeSigningKey = { kid : '123' , rsaPublicKey : 'the_rsa_public_key' } ;
2023-02-07 22:45:30 +11:00
spyOn ( authUtils , 'getHeaderFromToken' ) . and . callFake ( ( ) => fakeDecodedToken ) ;
2023-12-10 02:42:40 +01:00
spyOn ( authUtils , 'getSigningKey' ) . and . resolveTo ( fakeSigningKey ) ;
2021-02-24 14:36:24 +01:00
spyOn ( jwt , 'verify' ) . and . callFake ( ( ) => fakeClaim ) ;
try {
await facebook . validateAuthData (
{ id : 'the_user_id' , token : 'the_token' } ,
{ clientId : 'secret' }
) ;
fail ( ) ;
} catch ( e ) {
expect ( e . message ) . toBe ( 'auth data is invalid for this user.' ) ;
}
} ) ;
} ) ;
2023-06-24 01:57:57 +10:00
describe ( 'OTP TOTP auth adatper' , ( ) => {
const headers = {
'Content-Type' : 'application/json' ,
'X-Parse-Application-Id' : 'test' ,
'X-Parse-REST-API-Key' : 'rest' ,
} ;
beforeEach ( async ( ) => {
await reconfigureServer ( {
auth : {
mfa : {
enabled : true ,
options : [ 'TOTP' ] ,
algorithm : 'SHA1' ,
digits : 6 ,
period : 30 ,
} ,
} ,
} ) ;
} ) ;
it ( 'can enroll' , async ( ) => {
const user = await Parse . User . signUp ( 'username' , 'password' ) ;
const OTPAuth = require ( 'otpauth' ) ;
const secret = new OTPAuth . Secret ( ) ;
const totp = new OTPAuth . TOTP ( {
algorithm : 'SHA1' ,
digits : 6 ,
period : 30 ,
secret ,
} ) ;
const token = totp . generate ( ) ;
await user . save (
{ authData : { mfa : { secret : secret . base32 , token } } } ,
{ sessionToken : user . getSessionToken ( ) }
) ;
const response = user . get ( 'authDataResponse' ) ;
expect ( response . mfa ) . toBeDefined ( ) ;
expect ( response . mfa . recovery ) . toBeDefined ( ) ;
2023-07-07 01:22:18 +10:00
expect ( response . mfa . recovery . split ( ',' ) . length ) . toEqual ( 2 ) ;
2023-06-24 01:57:57 +10:00
await user . fetch ( ) ;
2023-07-07 01:22:18 +10:00
expect ( user . get ( 'authData' ) . mfa ) . toEqual ( { status : 'enabled' } ) ;
2023-06-24 01:57:57 +10:00
} ) ;
it ( 'can login with valid token' , async ( ) => {
const user = await Parse . User . signUp ( 'username' , 'password' ) ;
const OTPAuth = require ( 'otpauth' ) ;
const secret = new OTPAuth . Secret ( ) ;
const totp = new OTPAuth . TOTP ( {
algorithm : 'SHA1' ,
digits : 6 ,
period : 30 ,
secret ,
} ) ;
const token = totp . generate ( ) ;
await user . save (
{ authData : { mfa : { secret : secret . base32 , token } } } ,
{ sessionToken : user . getSessionToken ( ) }
) ;
const response = await request ( {
headers ,
method : 'POST' ,
url : 'http://localhost:8378/1/login' ,
body : JSON . stringify ( {
username : 'username' ,
password : 'password' ,
authData : {
2023-07-07 01:22:18 +10:00
mfa : {
token : totp . generate ( ) ,
} ,
2023-06-24 01:57:57 +10:00
} ,
} ) ,
} ) . then ( res => res . data ) ;
expect ( response . objectId ) . toEqual ( user . id ) ;
expect ( response . sessionToken ) . toBeDefined ( ) ;
2023-07-07 01:22:18 +10:00
expect ( response . authData ) . toEqual ( { mfa : { status : 'enabled' } } ) ;
2023-06-24 01:57:57 +10:00
expect ( Object . keys ( response ) . sort ( ) ) . toEqual (
[
'objectId' ,
'username' ,
'createdAt' ,
'updatedAt' ,
'authData' ,
'ACL' ,
'sessionToken' ,
'authDataResponse' ,
] . sort ( )
) ;
} ) ;
it ( 'can change OTP with valid token' , async ( ) => {
const user = await Parse . User . signUp ( 'username' , 'password' ) ;
const OTPAuth = require ( 'otpauth' ) ;
const secret = new OTPAuth . Secret ( ) ;
const totp = new OTPAuth . TOTP ( {
algorithm : 'SHA1' ,
digits : 6 ,
period : 30 ,
secret ,
} ) ;
const token = totp . generate ( ) ;
await user . save (
{ authData : { mfa : { secret : secret . base32 , token } } } ,
{ sessionToken : user . getSessionToken ( ) }
) ;
const new _secret = new OTPAuth . Secret ( ) ;
const new _totp = new OTPAuth . TOTP ( {
algorithm : 'SHA1' ,
digits : 6 ,
period : 30 ,
secret : new _secret ,
} ) ;
const new _token = new _totp . generate ( ) ;
await user . save (
{
authData : { mfa : { secret : new _secret . base32 , token : new _token , old : totp . generate ( ) } } ,
} ,
{ sessionToken : user . getSessionToken ( ) }
) ;
await user . fetch ( { useMasterKey : true } ) ;
expect ( user . get ( 'authData' ) . mfa . secret ) . toEqual ( new _secret . base32 ) ;
} ) ;
2023-07-07 01:22:18 +10:00
it ( 'cannot change OTP with invalid token' , async ( ) => {
const user = await Parse . User . signUp ( 'username' , 'password' ) ;
const OTPAuth = require ( 'otpauth' ) ;
const secret = new OTPAuth . Secret ( ) ;
const totp = new OTPAuth . TOTP ( {
algorithm : 'SHA1' ,
digits : 6 ,
period : 30 ,
secret ,
} ) ;
const token = totp . generate ( ) ;
await user . save (
{ authData : { mfa : { secret : secret . base32 , token } } } ,
{ sessionToken : user . getSessionToken ( ) }
) ;
const new _secret = new OTPAuth . Secret ( ) ;
const new _totp = new OTPAuth . TOTP ( {
algorithm : 'SHA1' ,
digits : 6 ,
period : 30 ,
secret : new _secret ,
} ) ;
const new _token = new _totp . generate ( ) ;
await expectAsync (
user . save (
{
authData : { mfa : { secret : new _secret . base32 , token : new _token , old : '123' } } ,
} ,
{ sessionToken : user . getSessionToken ( ) }
)
) . toBeRejectedWith ( new Parse . Error ( Parse . Error . OTHER _CAUSE , 'Invalid MFA token' ) ) ;
await user . fetch ( { useMasterKey : true } ) ;
expect ( user . get ( 'authData' ) . mfa . secret ) . toEqual ( secret . base32 ) ;
} ) ;
2023-06-24 01:57:57 +10:00
it ( 'future logins require TOTP token' , async ( ) => {
const user = await Parse . User . signUp ( 'username' , 'password' ) ;
const OTPAuth = require ( 'otpauth' ) ;
const secret = new OTPAuth . Secret ( ) ;
const totp = new OTPAuth . TOTP ( {
algorithm : 'SHA1' ,
digits : 6 ,
period : 30 ,
secret ,
} ) ;
const token = totp . generate ( ) ;
await user . save (
{ authData : { mfa : { secret : secret . base32 , token } } } ,
{ sessionToken : user . getSessionToken ( ) }
) ;
await expectAsync ( Parse . User . logIn ( 'username' , 'password' ) ) . toBeRejectedWith (
new Parse . Error ( Parse . Error . OTHER _CAUSE , 'Missing additional authData mfa' )
) ;
} ) ;
it ( 'future logins reject incorrect TOTP token' , async ( ) => {
const user = await Parse . User . signUp ( 'username' , 'password' ) ;
const OTPAuth = require ( 'otpauth' ) ;
const secret = new OTPAuth . Secret ( ) ;
const totp = new OTPAuth . TOTP ( {
algorithm : 'SHA1' ,
digits : 6 ,
period : 30 ,
secret ,
} ) ;
const token = totp . generate ( ) ;
await user . save (
{ authData : { mfa : { secret : secret . base32 , token } } } ,
{ sessionToken : user . getSessionToken ( ) }
) ;
await expectAsync (
request ( {
headers ,
method : 'POST' ,
url : 'http://localhost:8378/1/login' ,
body : JSON . stringify ( {
username : 'username' ,
password : 'password' ,
authData : {
2023-07-07 01:22:18 +10:00
mfa : {
token : 'abcd' ,
} ,
2023-06-24 01:57:57 +10:00
} ,
} ) ,
} ) . catch ( e => {
throw e . data ;
} )
) . toBeRejectedWith ( { code : Parse . Error . SCRIPT _FAILED , error : 'Invalid MFA token' } ) ;
} ) ;
} ) ;
describe ( 'OTP SMS auth adatper' , ( ) => {
const headers = {
'Content-Type' : 'application/json' ,
'X-Parse-Application-Id' : 'test' ,
'X-Parse-REST-API-Key' : 'rest' ,
} ;
let code ;
let mobile ;
const mfa = {
enabled : true ,
options : [ 'SMS' ] ,
sendSMS ( smsCode , number ) {
expect ( smsCode ) . toBeDefined ( ) ;
expect ( number ) . toBeDefined ( ) ;
expect ( smsCode . length ) . toEqual ( 6 ) ;
code = smsCode ;
mobile = number ;
} ,
digits : 6 ,
period : 30 ,
} ;
beforeEach ( async ( ) => {
code = '' ;
mobile = '' ;
await reconfigureServer ( {
auth : {
mfa ,
} ,
} ) ;
} ) ;
it ( 'can enroll' , async ( ) => {
const user = await Parse . User . signUp ( 'username' , 'password' ) ;
const sessionToken = user . getSessionToken ( ) ;
const spy = spyOn ( mfa , 'sendSMS' ) . and . callThrough ( ) ;
await user . save ( { authData : { mfa : { mobile : '+11111111111' } } } , { sessionToken } ) ;
await user . fetch ( { sessionToken } ) ;
2023-07-07 01:22:18 +10:00
expect ( user . get ( 'authData' ) ) . toEqual ( { mfa : { status : 'disabled' } } ) ;
2023-06-24 01:57:57 +10:00
expect ( spy ) . toHaveBeenCalledWith ( code , '+11111111111' ) ;
await user . fetch ( { useMasterKey : true } ) ;
const authData = user . get ( 'authData' ) . mfa ? . pending ;
expect ( authData ) . toBeDefined ( ) ;
expect ( authData [ '+11111111111' ] ) . toBeDefined ( ) ;
expect ( Object . keys ( authData [ '+11111111111' ] ) ) . toEqual ( [ 'token' , 'expiry' ] ) ;
await user . save ( { authData : { mfa : { mobile , token : code } } } , { sessionToken } ) ;
await user . fetch ( { sessionToken } ) ;
2023-07-07 01:22:18 +10:00
expect ( user . get ( 'authData' ) ) . toEqual ( { mfa : { status : 'enabled' } } ) ;
2023-06-24 01:57:57 +10:00
} ) ;
it ( 'future logins require SMS code' , async ( ) => {
const user = await Parse . User . signUp ( 'username' , 'password' ) ;
const spy = spyOn ( mfa , 'sendSMS' ) . and . callThrough ( ) ;
await user . save (
{ authData : { mfa : { mobile : '+11111111111' } } } ,
{ sessionToken : user . getSessionToken ( ) }
) ;
await user . save (
{ authData : { mfa : { mobile , token : code } } } ,
{ sessionToken : user . getSessionToken ( ) }
) ;
spy . calls . reset ( ) ;
await expectAsync ( Parse . User . logIn ( 'username' , 'password' ) ) . toBeRejectedWith (
new Parse . Error ( Parse . Error . OTHER _CAUSE , 'Missing additional authData mfa' )
) ;
const res = await request ( {
headers ,
method : 'POST' ,
url : 'http://localhost:8378/1/login' ,
body : JSON . stringify ( {
username : 'username' ,
password : 'password' ,
authData : {
2023-07-07 01:22:18 +10:00
mfa : {
token : 'request' ,
} ,
2023-06-24 01:57:57 +10:00
} ,
} ) ,
} ) . catch ( e => e . data ) ;
expect ( res ) . toEqual ( { code : Parse . Error . SCRIPT _FAILED , error : 'Please enter the token' } ) ;
expect ( spy ) . toHaveBeenCalledWith ( code , '+11111111111' ) ;
const response = await request ( {
headers ,
method : 'POST' ,
url : 'http://localhost:8378/1/login' ,
body : JSON . stringify ( {
username : 'username' ,
password : 'password' ,
authData : {
2023-07-07 01:22:18 +10:00
mfa : {
token : code ,
} ,
2023-06-24 01:57:57 +10:00
} ,
} ) ,
} ) . then ( res => res . data ) ;
expect ( response . objectId ) . toEqual ( user . id ) ;
expect ( response . sessionToken ) . toBeDefined ( ) ;
2023-07-07 01:22:18 +10:00
expect ( response . authData ) . toEqual ( { mfa : { status : 'enabled' } } ) ;
2023-06-24 01:57:57 +10:00
expect ( Object . keys ( response ) . sort ( ) ) . toEqual (
[
'objectId' ,
'username' ,
'createdAt' ,
'updatedAt' ,
'authData' ,
'ACL' ,
'sessionToken' ,
'authDataResponse' ,
] . sort ( )
) ;
} ) ;
it ( 'partially enrolled users can still login' , async ( ) => {
const user = await Parse . User . signUp ( 'username' , 'password' ) ;
await user . save ( { authData : { mfa : { mobile : '+11111111111' } } } ) ;
const spy = spyOn ( mfa , 'sendSMS' ) . and . callThrough ( ) ;
await Parse . User . logIn ( 'username' , 'password' ) ;
expect ( spy ) . not . toHaveBeenCalled ( ) ;
} ) ;
} ) ;