-
Notifications
You must be signed in to change notification settings - Fork 61
JS SDK: Facebook/Google OAuth Redirect #145
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
Conversation
edaniels
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice work so far. Have some changes to make for this pass. This still needs unit tests as well.
| * @returns {string} The encoded object | ||
| */ | ||
| private uriEncodeObject(obj: object): string { | ||
| return encodeURIComponent(base64.btoa(JSON.stringify(obj))); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[remove] can you please use b64DecodeUnicode from JWT.ts. Feel free to move it somewhere else. Also remove the base64 package from our deps. That one you included isn't good for us AFAIK
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
packages/browser/core/package.json
Outdated
| ], | ||
| "license": "Apache-2.0", | ||
| "dependencies": { | ||
| "Base64": "^1.0.1", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
remove
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
| /** | ||
| * The contents of this credential as they will be passed to the Stitch server. | ||
| */ | ||
| public constructor(public readonly authInfo: AuthInfo) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[q] does this just create a property and set it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, similar to how kotlin's data classes function
| newAuthInfo = credential.authInfo; | ||
| } else { | ||
| if (!response || !response.body) { | ||
| throw new StitchError("response was undefined"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a more specific error you can use? This needs a better error message as it's pretty vague as to where this is coming from
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah, this should be a StitchServiceError with an unknown error code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
| * requesting the user profile in a separate request. | ||
| */ | ||
| private processLoginResponse( | ||
| private processLogin( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this name should still stay
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In addition to that, we should actually pass a response here and we should instead of creating an auth info in the credential, have the response body marshaled.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps a better solution is to have this method stay as processLogin but have it accept an ApiAuthInfo or AuthInfo instead of a Response, and move the response decoding logic to a method called processLoginResponse which accepts a non-optional Response. That way, the codepaths for the non-redirect logins can stay the same, whereas the link code path can call processLogin directly with the auth info it reconstructs from the URL. The credential would then only need to hold the provider type and name. Thoughts @edaniels ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 handled offline
| ); | ||
| } | ||
|
|
||
| if (!redirectState.found) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
all errors below here need better error messages and removal of StitchClient
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
|
|
||
| // If we get here, the state is valid - set auth appropriately. | ||
| return this.loginWithCredentialInternal( | ||
| new StitchAuthResponseCredential(redirectState.ua) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure this will completely work as written. You're missing the provider type and provider name, both of which need to be stored and retrieved (only one is stored). Please add a unit test that proves this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should convert the ua here into the servers JSON form. An encode on ApiAuthInfo (ApiAuthInfo.toJSON) would work. Add a unit test for it's encoding.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The conversion should probably be done within CoreStitchAuth. Browser should be agnostic to ApiAuthInfo
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure that makes sense
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
| */ | ||
| public readonly material: { [key: string]: string }; | ||
|
|
||
| /** |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The contents aren't really passed to the stitch server. AuthInfo is a result to clients, not to the server. we only send access and refresh tokens back.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This wasn't updated
| credential: StitchRedirectCredential | ||
| ): Promise<void> { | ||
| this.auth.setRedirectFragment( | ||
| RedirectFragmentFields.LinkUser, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't get why this is being set. This should eliminate the need for the StitchUserCodec and all changes made in in StitchUser.ts
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 unset
|
|
||
| const state = generateState(); | ||
| auth.setRedirectFragment(RedirectFragmentFields.State, state); | ||
| auth.setRedirectFragment(RedirectKeys.Type, redirectType); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like this isn't ever used again
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
adamchel
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems really close, I agree with most of Eric's comments, but I disagreed in a few places. Let's maybe discuss in person when we're all in the office?
| ); | ||
| } | ||
|
|
||
| public hasRedirect(): boolean { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would disagree with you @edaniels . by default, the login/link with redirect redirects to the page from which it was called, so if we don't have this method, I feel like we should get rid of that default behavior and force people to pick a redirect destination as part of the login/link method. (EDIT: Basically what I'm saying is we should make the redirect URL in FacebookRedirectCredential and GoogleRedirectCredential non-optional, if we're getting rid of hasRedirect(). I still think we should have it though, since the 3.0 SDKs handled redirects back to the same page just fine.)
| newAuthInfo = credential.authInfo; | ||
| } else { | ||
| if (!response || !response.body) { | ||
| throw new StitchError("response was undefined"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah, this should be a StitchServiceError with an unknown error code.
| * requesting the user profile in a separate request. | ||
| */ | ||
| private processLoginResponse( | ||
| private processLogin( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps a better solution is to have this method stay as processLogin but have it accept an ApiAuthInfo or AuthInfo instead of a Response, and move the response decoding logic to a method called processLoginResponse which accepts a non-optional Response. That way, the codepaths for the non-redirect logins can stay the same, whereas the link code path can call processLogin directly with the auth info it reconstructs from the URL. The credential would then only need to hold the provider type and name. Thoughts @edaniels ?
|
|
||
| if (!window.location || !window.location.hash) { | ||
| cleanup(); | ||
| return Promise.reject(new StitchRedirectError("window location hash was undefined")); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we should return a null user.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why?
| ourState | ||
| ); | ||
|
|
||
| if (redirectState.lastError || (redirectState.found && !redirectProvider)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if we found no redirect info related or stitch, this should return null. specifically, it should look for client app id which ill add to the server now
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why null?
| const { redirectUrl, state } = prepareRedirect( | ||
| this.auth, | ||
| credential, | ||
| RedirectTypes.Login |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is the wrong type but I think you don't even need this enum
edaniels
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking very nice. Just have a bunch of trivial nits. Will re-review once tests are in 😎
package-lock.json
Outdated
| "long": "^3.2.0" | ||
| } | ||
| }, | ||
| "Base64": { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[shrug] You need to remove this manually or rm -rf node_modules and npm install again.
|
|
||
| loginWithRedirect(credential: StitchRedirectCredential): void; | ||
|
|
||
| handleRedirectResult(): Promise<StitchUser>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Where did hasRedirectResult go?
| ClientAppId = "_stitch_client_app_id", | ||
| } | ||
|
|
||
| export default RedirectFragmentFields; No newline at end of file |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nit] newline
| authRoutes: StitchAuthRoutes, | ||
| storage: Storage, | ||
| appInfo: StitchAppClientInfo | ||
| public readonly browserAuthRoutes: StitchBrowserAppAuthRoutes, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nit] private
| ); | ||
| } | ||
|
|
||
| public linkWithRedirect( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nit] linkUserWithRedirectInternal Users aren't supposed to call this directly
|
|
||
| import { Storage } from "../../../internal/common/Storage"; | ||
|
|
||
| import AuthInfo from "../AuthInfo"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add some simple encode decode tests for this por favor.
| return this.doGetUserProfile() | ||
| .then(profile => { | ||
| // readonlyly set the info and user | ||
| // readonly set the info and user |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what's readonly?
| let userPassClient = client.auth.getProviderClient(UserPasswordAuthProviderClient.factory) | ||
| componentDidMount() { | ||
| const client = Stitch.initializeDefaultAppClient( | ||
| "test-js-sdk-eocey", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
remove
| private processLoginResponse(credential: StitchCredential, response: Response): Promise<TStitchUser> { | ||
| try { | ||
| if (!response) { | ||
| throw new StitchServiceError("response was undefined"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nit] add some context to this error
| ); | ||
|
|
||
| describe("StitchAuthImpl", () => { | ||
| it("should handleRedirect", async () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test should be updated or go away after your tests.
|
@edaniels PTAL |
| if (!response) { | ||
| throw new StitchServiceError("response was undefined"); | ||
| throw new StitchServiceError( | ||
| `the login response could not be processed for credential: ${credential};` + |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[minor] don't log the credential. may contain sensitive info.
| expect((credential as StitchAuthCredential).authInfo).toEqual(redirectResult.ua); | ||
| expect(credential.providerName).toEqual("test_provider_name"); | ||
| expect(credential.providerType).toEqual("test_provider_type"); | ||
| expect((credential as StitchAuthCredential).authInfo).toEqual(redirectFragmentResult); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[request] go a little plus ultra here and mock the profile parts and verify you are logged in with the info above
edaniels
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM. Just have two requests for you.
adamchel
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A few nits but LGTM. We're so close to being done!
|
|
||
| export default class StitchRedirectError extends StitchError { | ||
| constructor(msg: string) { super(msg); } | ||
| } No newline at end of file |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nit] newline
| }); | ||
|
|
||
| let hasBeenCalled = false; | ||
| const hasBeenCalled = false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nit] do you actually use this anywhere? I think you can get rid of it
| } | ||
|
|
||
| /** | ||
| * Processes the response of the login/link request, setting the authentication state if appropriate, and |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nit] move this comment to processLoginResponse, and if you have time add a comment here describing what this function does now
No description provided.