Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Missing documentation for Custom Auth #963

Merged
merged 4 commits into from
Oct 18, 2019
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/AnonymousUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ const AnonymousUtils = {
*/
logIn(options?: RequestOptions) {
const provider = this._getAuthProvider();
return ParseUser._logInWith(provider.getAuthType(), provider.getAuthData(), options);
return ParseUser.logInWith(provider.getAuthType(), provider.getAuthData(), options);
dplewis marked this conversation as resolved.
Show resolved Hide resolved
},

/**
Expand All @@ -84,7 +84,7 @@ const AnonymousUtils = {
*/
link(user: ParseUser, options?: RequestOptions) {
const provider = this._getAuthProvider();
return user._linkWith(provider.getAuthType(), provider.getAuthData(), options);
return user.linkWith(provider.getAuthType(), provider.getAuthData(), options);
},

_getAuthProvider() {
Expand Down
8 changes: 4 additions & 4 deletions src/FacebookUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -170,10 +170,10 @@ const FacebookUtils = {
);
}
requestedPermissions = permissions;
return ParseUser._logInWith('facebook', options);
return ParseUser.logInWith('facebook', options);
}
const authData = { authData: permissions };
return ParseUser._logInWith('facebook', authData, options);
return ParseUser.logInWith('facebook', authData, options);
},

/**
Expand Down Expand Up @@ -210,10 +210,10 @@ const FacebookUtils = {
);
}
requestedPermissions = permissions;
return user._linkWith('facebook', options);
return user.linkWith('facebook', options);
}
const authData = { authData: permissions };
return user._linkWith('facebook', authData, options);
return user.linkWith('facebook', authData, options);
},

/**
Expand Down
52 changes: 45 additions & 7 deletions src/ParseUser.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,10 +74,21 @@ class ParseUser extends ParseObject {
}

/**
* Unlike in the Android/iOS SDKs, logInWith is unnecessary, since you can
* call linkWith on the user (even if it doesn't exist yet on the server).
* Parse allows you to link your users with {@link https://docs.parseplatform.org/parse-server/guide/#oauth-and-3rd-party-authentication 3rd party authentication}, enabling
* your users to sign up or log into your application using their existing identities.
* Since 2.9.0
*
* @see {@link https://docs.parseplatform.org/js/guide/#linking-users Linking Users}
* @param {String|AuthProvider} provider Name of auth provider or {@link https://parseplatform.org/Parse-SDK-JS/api/master/AuthProvider.html AuthProvider}
* @param {Object} options
* <ul>
* <li>If provider is string, options is {@link http://docs.parseplatform.org/parse-server/guide/#supported-3rd-party-authentications authData}
* <li>If provider is AuthProvider, options is saveOpts
* </ul>
* @param {Object} saveOpts useMasterKey / sessionToken
* @return {Promise} A promise that is fulfilled with the user is linked
*/
_linkWith(provider: any, options: { authData?: AuthData }, saveOpts?: FullOptions = {}): Promise<ParseUser> {
linkWith(provider: any, options: { authData?: AuthData }, saveOpts?: FullOptions = {}): Promise<ParseUser> {
saveOpts.sessionToken = saveOpts.sessionToken || this.getSessionToken() || '';
let authType;
if (typeof provider === 'string') {
Expand Down Expand Up @@ -118,7 +129,7 @@ class ParseUser extends ParseObject {
success: (provider, result) => {
const opts = {};
opts.authData = result;
this._linkWith(provider, opts, saveOpts).then(() => {
this.linkWith(provider, opts, saveOpts).then(() => {
resolve(this);
}, (error) => {
reject(error);
Expand All @@ -132,6 +143,13 @@ class ParseUser extends ParseObject {
}
}

/**
* @deprecated since 2.9.0 see {@link https://parseplatform.org/Parse-SDK-JS/api/master/Parse.User.html#linkWith linkWith}
*/
_linkWith(provider: any, options: { authData?: AuthData }, saveOpts?: FullOptions = {}): Promise<ParseUser> {
return this.linkWith(provider, options, saveOpts);
}

/**
* Synchronizes auth data for a provider (e.g. puts the access token in the
* right place to be used by the Facebook SDK).
Expand Down Expand Up @@ -200,7 +218,7 @@ class ParseUser extends ParseObject {
if (typeof provider === 'string') {
provider = authProviders[provider];
}
return this._linkWith(provider, { authData: null }, options).then(() => {
return this.linkWith(provider, { authData: null }, options).then(() => {
this._synchronizeAuthData(provider);
return Promise.resolve(this);
});
Expand Down Expand Up @@ -687,8 +705,13 @@ class ParseUser extends ParseObject {
return controller.hydrate(userJSON);
}

/**
* Static version of {@link https://parseplatform.org/Parse-SDK-JS/api/master/Parse.User.html#linkWith linkWith}
* @static
*/
static logInWith(provider: any, options: { authData?: AuthData }, saveOpts?: FullOptions) {
return ParseUser._logInWith(provider, options, saveOpts);
const user = new ParseUser();
return user.linkWith(provider, options, saveOpts);
}

/**
Expand Down Expand Up @@ -796,6 +819,17 @@ class ParseUser extends ParseObject {
canUseCurrentUser = false;
}

/**
* When registering users with {@link https://parseplatform.org/Parse-SDK-JS/api/master/Parse.User.html#linkWith linkWith} a basic auth provider
* is automatically created for you.
*
* For advanced authentication, you can register an Auth provider to
* implement custom authentication, deauthentication.
*
* @see {@link https://parseplatform.org/Parse-SDK-JS/api/master/AuthProvider.html AuthProvider}
* @see {@link https://docs.parseplatform.org/js/guide/#custom-authentication-module Custom Authentication Module}
* @static
*/
static _registerAuthenticationProvider(provider: any) {
authProviders[provider.getAuthType()] = provider;
// Synchronize the current user with the auth provider.
Expand All @@ -806,9 +840,13 @@ class ParseUser extends ParseObject {
});
}

/**
* @deprecated since 2.9.0 see {@link https://parseplatform.org/Parse-SDK-JS/api/master/Parse.User.html#logInWith logInWith}
* @static
*/
static _logInWith(provider: any, options: { authData?: AuthData }, saveOpts?: FullOptions) {
const user = new ParseUser();
return user._linkWith(provider, options, saveOpts);
return user.linkWith(provider, options, saveOpts);
}

static _clearCache() {
Expand Down
16 changes: 8 additions & 8 deletions src/__tests__/AnonymousUtils-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ class MockUser {
this.attributes = {};
}
_isLinked() {}
_linkWith() {}
linkWith() {}
static _registerAuthenticationProvider() {}
static _logInWith() {}
static logInWith() {}
}

jest.setMock('../ParseUser', MockUser);
Expand Down Expand Up @@ -71,18 +71,18 @@ describe('AnonymousUtils', () => {

it('can link user', () => {
const user = new MockUser();
jest.spyOn(user, '_linkWith');
jest.spyOn(user, 'linkWith');
AnonymousUtils.link(user);
expect(user._linkWith).toHaveBeenCalledTimes(1);
expect(user._linkWith).toHaveBeenCalledWith('anonymous', mockProvider.getAuthData(), undefined);
expect(user.linkWith).toHaveBeenCalledTimes(1);
expect(user.linkWith).toHaveBeenCalledWith('anonymous', mockProvider.getAuthData(), undefined);
expect(AnonymousUtils._getAuthProvider).toHaveBeenCalledTimes(1);
});

it('can login user', () => {
jest.spyOn(MockUser, '_logInWith');
jest.spyOn(MockUser, 'logInWith');
AnonymousUtils.logIn();
expect(MockUser._logInWith).toHaveBeenCalledTimes(1);
expect(MockUser._logInWith).toHaveBeenCalledWith('anonymous', mockProvider.getAuthData(), undefined);
expect(MockUser.logInWith).toHaveBeenCalledTimes(1);
expect(MockUser.logInWith).toHaveBeenCalledWith('anonymous', mockProvider.getAuthData(), undefined);
expect(AnonymousUtils._getAuthProvider).toHaveBeenCalledTimes(1);
});
});
24 changes: 12 additions & 12 deletions src/__tests__/FacebookUtils-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ class MockUser {
this.attributes = {};
}
_isLinked() {}
_linkWith() {}
linkWith() {}
_unlinkFrom() {}
static _registerAuthenticationProvider() {}
static _logInWith() {}
static logInWith() {}
}

jest.setMock('../ParseUser', MockUser);
Expand Down Expand Up @@ -110,17 +110,17 @@ describe('FacebookUtils', () => {
const authData = {
id: '1234'
};
jest.spyOn(user, '_linkWith');
jest.spyOn(user, 'linkWith');
await FacebookUtils.link(user, authData);
expect(user._linkWith).toHaveBeenCalledWith('facebook', { authData: { id: '1234' } }, undefined);
expect(user.linkWith).toHaveBeenCalledWith('facebook', { authData: { id: '1234' } }, undefined);
});

it('can link with options', async () => {
FacebookUtils.init();
const user = new MockUser();
jest.spyOn(user, '_linkWith');
jest.spyOn(user, 'linkWith');
await FacebookUtils.link(user, {}, { useMasterKey: true });
expect(user._linkWith).toHaveBeenCalledWith('facebook', { authData: {} }, { useMasterKey: true });
expect(user.linkWith).toHaveBeenCalledWith('facebook', { authData: {} }, { useMasterKey: true });
});

it('can check isLinked', async () => {
Expand All @@ -147,23 +147,23 @@ describe('FacebookUtils', () => {

it('can login with permission string', async () => {
FacebookUtils.init();
jest.spyOn(MockUser, '_logInWith');
jest.spyOn(MockUser, 'logInWith');
await FacebookUtils.logIn('public_profile');
expect(MockUser._logInWith).toHaveBeenCalledTimes(1);
expect(MockUser.logInWith).toHaveBeenCalledTimes(1);
});

it('can login with authData', async () => {
FacebookUtils.init();
jest.spyOn(MockUser, '_logInWith');
jest.spyOn(MockUser, 'logInWith');
await FacebookUtils.logIn({ id: '1234' });
expect(MockUser._logInWith).toHaveBeenCalledTimes(1);
expect(MockUser.logInWith).toHaveBeenCalledTimes(1);
});

it('can login with options', async () => {
FacebookUtils.init();
jest.spyOn(MockUser, '_logInWith');
jest.spyOn(MockUser, 'logInWith');
await FacebookUtils.logIn({}, { useMasterKey: true });
expect(MockUser._logInWith).toHaveBeenCalledWith('facebook', { authData: {} }, {useMasterKey: true });
expect(MockUser.logInWith).toHaveBeenCalledWith('facebook', { authData: {} }, {useMasterKey: true });
});

it('provider getAuthType', async () => {
Expand Down
14 changes: 7 additions & 7 deletions src/__tests__/ParseUser-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -837,25 +837,25 @@ describe('ParseUser', () => {
const provider = AnonymousUtils._getAuthProvider();
ParseUser._registerAuthenticationProvider(provider);
const user = new ParseUser();
jest.spyOn(user, '_linkWith');
jest.spyOn(user, 'linkWith');
user._unlinkFrom(provider);
expect(user._linkWith).toHaveBeenCalledTimes(1);
expect(user._linkWith).toHaveBeenCalledWith(provider, { authData: null }, undefined);
expect(user.linkWith).toHaveBeenCalledTimes(1);
expect(user.linkWith).toHaveBeenCalledWith(provider, { authData: null }, undefined);
});

it('can unlink with options', async () => {
const provider = AnonymousUtils._getAuthProvider();
ParseUser._registerAuthenticationProvider(provider);
const user = new ParseUser();
jest.spyOn(user, '_linkWith')
jest.spyOn(user, 'linkWith')
.mockImplementationOnce((authProvider, authData, saveOptions) => {
expect(authProvider).toEqual(provider);
expect(authData).toEqual({ authData: null});
expect(saveOptions).toEqual({ useMasterKey: true });
return Promise.resolve();
});
user._unlinkFrom(provider.getAuthType(), { useMasterKey: true });
expect(user._linkWith).toHaveBeenCalledTimes(1);
expect(user.linkWith).toHaveBeenCalledTimes(1);
});

it('can destroy anonymous user when login new user', async () => {
Expand Down Expand Up @@ -1003,10 +1003,10 @@ describe('ParseUser', () => {
await user._linkWith('testProvider', { authData: { id: 'test' } });
expect(user.get('authData')).toEqual({ testProvider: { id: 'test' } });

jest.spyOn(user, '_linkWith');
jest.spyOn(user, 'linkWith');

await user._unlinkFrom('testProvider');
const authProvider = user._linkWith.mock.calls[0][0];
const authProvider = user.linkWith.mock.calls[0][0];
expect(authProvider.getAuthType()).toBe('testProvider');
});
});
13 changes: 8 additions & 5 deletions src/interfaces/AuthProvider.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-disable */
dplewis marked this conversation as resolved.
Show resolved Hide resolved
/**
* Copyright (c) 2015-present, Parse, LLC.
* All rights reserved.
Expand All @@ -10,28 +11,30 @@

/**
* Interface declaration for Authentication Providers
*
* @interface AuthProvider
*/
export interface AuthProvider {
export class AuthProvider {
acinader marked this conversation as resolved.
Show resolved Hide resolved
/**
* Called when _linkWith isn't passed authData.
* Handle your own authentication here.
*
* @params {Object} options.success(provider, authData) or options.error(provider, error) on completion
*/
authenticate(options: any): void,
authenticate(options: any): void {}

/**
* (Optional) Called when service is unlinked.
* Handle any cleanup here.
*/
deauthenticate(): void,
deauthenticate(): void {}

/**
* Unique identifier for this Auth Provider.
*
* @return {String} identifier
*/
getAuthType(): string,
getAuthType(): string {}

/**
* Called when auth data is syncronized.
Expand All @@ -40,5 +43,5 @@ export interface AuthProvider {
* @params {Object} authData Data used when register provider
* @return {Boolean} Indicate if service should continue to be linked
*/
restoreAuthentication(authData: any): boolean,
restoreAuthentication(authData: any): boolean {}
}