-
Notifications
You must be signed in to change notification settings - Fork 945
Add popup util and bare-bones OAuthProvider #3335
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
Merged
Merged
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
/** | ||
* @license | ||
* Copyright 2019 Google LLC | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
import * as externs from '@firebase/auth-types-exp'; | ||
|
||
import { AuthErrorCode } from '../errors'; | ||
import { debugFail } from '../util/assert'; | ||
|
||
export type CustomParameters = Record<string, string>; | ||
|
||
interface CredentialParameters { | ||
idToken?: string; | ||
accessToken?: string; | ||
rawNonce?: string; | ||
} | ||
|
||
export class OAuthProvider implements externs.AuthProvider { | ||
defaultLanguageCode: string | null = null; | ||
private scopes: string[] = []; | ||
private customParameters: CustomParameters = {}; | ||
private constructor(readonly providerId: externs.ProviderId) {} | ||
static credentialFromResult( | ||
_userCredential: externs.UserCredential | ||
): externs.OAuthCredential | null { | ||
debugFail('not implemented'); | ||
} | ||
static credentialFromError( | ||
_error: AuthErrorCode | ||
): externs.OAuthCredential | null { | ||
debugFail('not implemented'); | ||
} | ||
static credentialFromJSON(_json: object): externs.OAuthCredential { | ||
debugFail('not implemented'); | ||
} | ||
|
||
credential(_params: CredentialParameters): externs.OAuthCredential { | ||
debugFail('not implemented'); | ||
} | ||
|
||
setDefaultLanguage(languageCode: string | null): void { | ||
this.defaultLanguageCode = languageCode; | ||
} | ||
|
||
setCustomParameters( | ||
customOAuthParameters: CustomParameters | ||
): externs.AuthProvider { | ||
this.customParameters = customOAuthParameters; | ||
return this; | ||
} | ||
|
||
getCustomParameters(): CustomParameters { | ||
avolkovi marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return this.customParameters; | ||
} | ||
|
||
addScope(scope: string): externs.AuthProvider { | ||
// If not already added, add scope to list. | ||
if (!this.scopes.includes(scope)) { | ||
this.scopes.push(scope); | ||
} | ||
return this; | ||
} | ||
|
||
getScopes(): string[] { | ||
return [...this.scopes]; | ||
} | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
/** | ||
* @license | ||
* Copyright 2020 Google LLC | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
const CHROME_IOS_UA = 'crios/'; | ||
const FIREFOX_UA = 'firefox/'; | ||
const IPHONE_UA = 'iphone'; | ||
const IPOD_UA = 'ipod'; | ||
const IPAD_UA = 'ipad'; | ||
|
||
interface NavigatorStandalone extends Navigator { | ||
standalone?: unknown; | ||
} | ||
|
||
export function _isFirefox(ua: string): boolean { | ||
return ua.includes(FIREFOX_UA); | ||
} | ||
|
||
export function _isChromeIOS(ua: string): boolean { | ||
return ua.includes(CHROME_IOS_UA); | ||
} | ||
|
||
export function _isIOS(ua: string): boolean { | ||
return ua.includes(IPHONE_UA) || ua.includes(IPOD_UA) || ua.includes(IPAD_UA); | ||
} | ||
|
||
export function _isIOSStandalone(ua: string): boolean { | ||
return _isIOS(ua) && !!(window.navigator as NavigatorStandalone)?.standalone; | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
/** | ||
* @license | ||
* Copyright 2020 Google LLC | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
export function _generateEventId(prefix?: string): string { | ||
return `${prefix ? prefix : ''}${Math.floor(Math.random() * 1000000000)}`; | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,180 @@ | ||
/** | ||
* @license | ||
* Copyright 2020 Google LLC. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
import { expect, use } from 'chai'; | ||
import * as sinon from 'sinon'; | ||
import * as sinonChai from 'sinon-chai'; | ||
|
||
import { FirebaseError } from '@firebase/util'; | ||
import * as utils from '@firebase/util'; | ||
|
||
import { _open, AuthPopup } from './popup'; | ||
|
||
use(sinonChai); | ||
|
||
describe('src/core/util/popup', () => { | ||
let windowOpenStub: sinon.SinonStub; | ||
let popupStub: sinon.SinonStubbedInstance<Window>; | ||
|
||
function setUA(ua: string): void { | ||
sinon.stub(utils, 'getUA').returns(ua); | ||
} | ||
|
||
function windowTarget(): string { | ||
return windowOpenStub.firstCall.args[1]; | ||
} | ||
|
||
function windowURL(): string { | ||
return windowOpenStub.firstCall.args[0]; | ||
} | ||
|
||
function windowOptions(): string { | ||
return windowOpenStub.firstCall.args[2]; | ||
} | ||
|
||
beforeEach(() => { | ||
windowOpenStub = sinon.stub(window, 'open'); | ||
popupStub = sinon.stub(({ | ||
focus: () => {}, | ||
close: () => {} | ||
} as unknown) as Window); | ||
windowOpenStub.returns(popupStub); | ||
}); | ||
|
||
afterEach(() => { | ||
sinon.restore(); | ||
}); | ||
|
||
it('sets target to name param if not chrome UA', () => { | ||
setUA('notchrome'); | ||
_open('appName', 'url', 'name'); | ||
expect(windowTarget()).to.eq('name'); | ||
}); | ||
|
||
it('sets target to _blank if on chrome IOS', () => { | ||
setUA('crios/'); | ||
_open('appName', 'url', 'name'); | ||
expect(windowTarget()).to.eq('_blank'); | ||
}); | ||
|
||
it('sets the firefox url to a default if not provided', () => { | ||
setUA('firefox/'); | ||
_open('appName'); | ||
expect(windowURL()).to.eq('http://localhost'); | ||
}); | ||
|
||
it('sets the firefox url to the value provided', () => { | ||
setUA('firefox/'); | ||
_open('appName', 'url'); | ||
expect(windowURL()).to.eq('url'); | ||
}); | ||
|
||
it('sets non-firefox url to empty if not provided', () => { | ||
setUA('not-ff/'); | ||
_open('appName'); | ||
expect(windowURL()).to.eq(''); | ||
}); | ||
|
||
it('sets non-firefox url to url if not provided', () => { | ||
setUA('not-ff/'); | ||
_open('appName', 'url'); | ||
expect(windowURL()).to.eq('url'); | ||
}); | ||
|
||
it('sets scrollbars to yes in popup', () => { | ||
setUA('firefox/'); | ||
_open('appName'); | ||
expect(windowOptions()).to.include('scrollbars=yes'); | ||
}); | ||
|
||
it('errors if the popup is blocked', () => { | ||
setUA(''); | ||
windowOpenStub.returns(undefined); | ||
expect(() => _open('appName')).to.throw( | ||
FirebaseError, | ||
'auth/popup-blocked' | ||
); | ||
}); | ||
|
||
it('builds the proper options string', () => { | ||
const screen = window.screen; | ||
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */ | ||
(window as any).sreen = { | ||
availHeight: 1000, | ||
availWidth: 2000 | ||
}; | ||
|
||
setUA(''); | ||
_open('appName'); | ||
const options = windowOptions() | ||
.split(',') | ||
.filter(s => !!s) | ||
.map(prop => prop.split('=')) | ||
.reduce<Record<string, string>>((accum, [prop, val]) => { | ||
accum[prop] = val; | ||
return accum; | ||
}, {}); | ||
|
||
expect(options).to.eql({ | ||
location: 'yes', | ||
resizable: 'yes', | ||
statusbar: 'yes', | ||
toolbar: 'no', | ||
width: '500', | ||
height: '600', | ||
top: '0', | ||
left: '0' | ||
}); | ||
|
||
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */ | ||
(window as any).screen = screen; | ||
}); | ||
|
||
it('calls focus on the new popup', () => { | ||
setUA(''); | ||
_open('appName'); | ||
expect(popupStub.focus).to.have.been.called; | ||
}); | ||
|
||
it('does not fail if window.focus errors', () => { | ||
popupStub.focus.throws(new Error('lol no')); | ||
setUA(''); | ||
expect(() => _open('appName')).not.to.throw(Error); | ||
}); | ||
|
||
context('resulting popup object', () => { | ||
let authPopup: AuthPopup; | ||
beforeEach(() => { | ||
setUA(''); | ||
authPopup = _open('appName'); | ||
}); | ||
|
||
it('has a window object', () => { | ||
expect(authPopup.window).to.eq(popupStub); | ||
}); | ||
|
||
it('calls through to the popup close', () => { | ||
authPopup.close(); | ||
expect(popupStub.close).to.have.been.called; | ||
}); | ||
|
||
it('close() does not error if underlying call errors', () => { | ||
popupStub.close.throws(new Error('not this time')); | ||
expect(() => authPopup.close()).not.to.throw(Error); | ||
}); | ||
}); | ||
}); |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.