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

App State Observable #168

Merged
merged 14 commits into from
Jun 16, 2021

Conversation

fearnycompknowhow
Copy link
Contributor

Description

My changes enable direct access to the appState that is returned from the handleRedirectCallback method.
This appState is made available through a new appState$ observable on the AuthService class.

References

The need for this feature has been voiced by a few other developers: https://community.auth0.com/t/handle-appstate-and-redirect-to-dynamic-url-after-successful-login-auth0-angular/50982

Testing

You can run both the unit tests and the e2e tests the normal way

Checklist

  • This change adds test coverage for new/changed/fixed functionality
  • I have added documentation for new/changed functionality in this PR or in auth0.com/docs
  • All active GitHub checks for tests, formatting, and security are passing
  • The correct base branch is being used, if not master

@fearnycompknowhow fearnycompknowhow requested a review from a team as a code owner June 12, 2021 00:03
@fearnycompknowhow fearnycompknowhow changed the title App State oObservable App State Observable Jun 12, 2021
Copy link
Member

@frederikprijck frederikprijck left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR, Keep in mind it is encourages to open an issue first so we can discuss this before the work is done.

I do want to add that appState is already available to your application today, but I agree it's not clear on how to do so:

  1. Start with importing AuthModule and setting skipRedirectCallback to true. This will ensure our SDK will not handle the redirect for you.
imports: [
  AuthModule.forRoot({ skipRedirectCallback: true })
]
  1. Call handleRedirectCallback yourself where appropriate, be sure this is only on the configured /callback route:
constructor(private authService: AuthService) {
  this.authService.handleRedirectCallback()
    .pipe(map(result => result?.appState))
    .subscribe(appState => /* use appState here */);
}

As you can see, calling handleRedirectCallback yourself gives you access to the appState without introducing any new code.

That said, I agree this is not convenient and I would prefer for people to not have to need to call handleRedirectCallback themselves if all they need is access to the appState.

README.md Outdated
}
```

> This method of saving application state will save it in Session Storage which is capable of holding multiple MBs worth of data, so you should have more than enough space for your needs
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is only true by default, but it could as well be cookies if useCookiesForTransactions is used. Might also make sense to ensure it's clear that this data is only persisted temporarily and will be removed again once being redirected back to your application.

I also believe this message makes it sounds like you can just use this to track any appState you like (as we have storage for multiple MBs worth of data anyway), while I still believe your application should not solely rely on Auth0's SDK to persist your state, you could as well implement something yourself outside of our SDK which gives you more control.

I think it makes more sense to drop the last part and only mention it get stored in Session Storage by default, but can be stored into a cookie when setting useCookiesForTransactions.

Because of the fact that we can also use cookies, I would (as mentioned earlier) remove any emphasis on deeply nested objects (which is also who I would prefer to not have this end up in the readme, as there are limitations to using appState and this SDK is not a state persisting library).

@@ -333,7 +340,10 @@ export class AuthService implements OnDestroy {
if (!isLoading) {
this.refreshState$.next();
}
const target = result?.appState?.target ?? '/';
const appState = result.appState;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reason why we changed result?.appState to be result.appState?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pardon me, this was just a simple oversight on my part

@@ -36,6 +38,12 @@ export class AppComponent {
ignoreCache: new FormControl(false),
});

ngOnInit(): void {
this.auth.appState$.subscribe((appState) => {
this.appState = appState.deeply.nested.value;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am confused with this example. The appState is the appState, and not some deeply nested value.
So I would either use this.appState = appState or this.someOtherProperty = appState.deeply.nested.value;

That said, even though it's good knowing this is possible, I would reduce the nesting here as I believe we want to avoid people using appState to track too much complexity. I would personally stick to a single property on the appState.

value: this.loginOptionsForm.value.appState,
},
},
},
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here, it confuses me that we are using appState.deeply.nested.value = this.loginOptionsForm.value.appState, it should either be appState: appState or appState.deeply.nested.value = this.loginOptionsForm.value.someValue.

However, I said before I would prefer to reduce the nesting.

const appState = result.appState;
const target = appState?.target ?? '/';

this.appStateSubject$.next(appState);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Even though the comments on the appState$ mention it emits (if any), yet it emits even if appState is null or undefined.

README.md Outdated
loginWithRedirect(): void {
this.auth.loginWithRedirect({
appState: {
// Specify which application state you would like to preserve here (yes, objects ARE allowed)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would be a bit more explicit on the sample and drop the emphasis on objects here.

appState: {
  myValue: 'My State to Track'
}

README.md Outdated

ngOnInit() {
this.auth.appState$.subscribe((appState) => {
// Your value will be available here
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would show what this means instead of the comment:

this.auth.appState$.subscribe((appState) => {
  console.log(appState.myValue);
});

README.md Outdated
@@ -121,6 +121,56 @@ On your template, provide a button that will allow the user to log in to the app
</button>
```

### Preserve application state through redirects
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would move this to our FAQ as I believe this is not a main feature of our SDK. We want to avoid that people rely to heavily on our SDK for tracking application state.

@fearnycompknowhow
Copy link
Contributor Author

@frederikprijck Sorry for not opening a discussion request first. I'll be sure to do that if I do any more work on this repo.

As far as the build issues go: the Cypress tests keep failing, because somewhere undefined is being typed into an input.
The only cause I can think of is that the call to Cypress.env('INTEGRATION_PASSWORD'); is returning undefined.

EMAIL and appState are the only other values that are being used for typing, but they are both defined statically, so I can't see where they would be getting set to undefined.

@frederikprijck
Copy link
Member

frederikprijck commented Jun 14, 2021

Thanks for making the changes 🚀 🔥
I will look into the Cypress build tomorrow morning!

@fearnycompknowhow
Copy link
Contributor Author

No problem. Let me know if I missed anything or if there is anything else you want changed

@frederikprijck
Copy link
Member

Cypress tests run fine locally. The reason they fail here is because we do not allow running them on forks.

Copy link
Member

@frederikprijck frederikprijck left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this PR 🔥

@fearnycompknowhow
Copy link
Contributor Author

No problem. It was my pleasure

@frederikprijck frederikprijck added the CH: Added PR is adding feature or functionality label Jun 16, 2021
@frederikprijck frederikprijck added this to the vNext milestone Jun 16, 2021
@frederikprijck frederikprijck merged commit e9b216b into auth0:master Jun 16, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CH: Added PR is adding feature or functionality
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants