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

Improve error handling and sign out flow when authenticating with DfE Sign-in #150

Merged
merged 8 commits into from
Nov 9, 2023

Conversation

malcolmbaig
Copy link
Contributor

Context

@steventux completed some work on AYTQ recently that improves error handling and sign out behaviour:

DFE-Digital/access-your-teaching-qualifications#365
https://github.com/DFE-Digital/access-your-teaching-qualifications/pull/

We want to implement this in CCBL as well.

Changes proposed in this pull request

The commit history provides the best summary of how the work in the PRs above has been extracted out and grouped.

In brief though:

  • Configure the dfe omniauth strategy to support signing out of DSI
  • Add a failure app to handle oauth-related errors
  • Add a more detailed 401 page

Guidance to review

Can be tested manually if your local CCBL is pointing to dev DSI.

Link to Trello card

https://trello.com/c/1Cfdo0ip/53-cbl-complete-the-dfe-signin-integration

Checklist

  • Attach to Trello card
  • Rebased main
  • Cleaned commit history
  • Tested by running locally

@malcolmbaig malcolmbaig marked this pull request as ready for review November 1, 2023 14:41
Extract methods to make the flow a little clearer (including the
difference in behaviour when bypass mode is active).

This also prepares the controller for further changes related to storing
the organisation details in the session, which we'll need to display a
more informative 401 page when authorisation fails.
- The id_token is needed to sign out of DSI properly when signing out of
  the service, so that users can change the organisation they sign in
  with, for example. We'll implement this in a subsequent commit.
- The organisation name is needed so we can display it on the 401 page.
- Use reset_session in the SignOutController to clear the various DSI
  attributes we set.
- Add a system spec for signing out.
When the user clicks the Sign out link in the header, we now sign them
out of DSI before sending them to the sign out page.

We do this with the following:

- Ensure the token_id is set in the sign out link in the header (this
  token is made available in the session cookie during the sign in
  phase).
- Add a dedicated strategy file (DfEOpenIDConnect) so we can extract
  this token and make it part of the post_logout_redirect_uri. This is
  required for the OIDC library used by DSI to redirect correctly after
  handling the sign out.
Amend the content so that we:

- include the name of the organisation the user is signed in as
- provide a sign out link with the appropriate token set to ensure a
  full DSI sign out occurs, followed by a redirect back to the service
Simplify the UI so that the "sign out and start again" link is the
only way for the user to continue.
Sometimes an error arises during the omniauth libary's communication
with the auth provider (DSI in this case). In those cases, we need some
kind of basic error handling.

Here we:

- Add an AuthFailuresController
- Configure omniauth to use it as a failure app
- Add specific handling for "sessionexpired" errors
- Add generic error handling for anything else
- Add a FlashMessageComponent so we can present a generic error message
  to the user
@malcolmbaig malcolmbaig force-pushed the 53-update-dsi-sign-out branch from 33c7a0a to 3af8dd7 Compare November 8, 2023 12:17
There are cases where the auth flow between this service and DSI can get
into an inconsistent state, resulting in CSRF and state errors from the
auth provider. For example:

- authenticate with an unauthorised organisation
- open a new tab to attempt a new sign in
- submit the form and get a state error from DSI

To resolve these cases, and to make the auth flow more robust, we do the
following:

- Reset the session on the sign in page instead of clearing specific
  session attributes in the omniauth callback controller. This gives us
  a simpler baseline state from which to attempt authentication,
  regardless of how the user arrives at the page (whether through direct
  navigation, or a redirect following an error).
- Add an oauth_failure param when redirecting after an oauth error, so
  that we can continue to display a flash message on the sign in page
  despite having reset the session.
- Add system specs for this behaviour.
@malcolmbaig malcolmbaig force-pushed the 53-update-dsi-sign-out branch from 3af8dd7 to ccc8cdf Compare November 8, 2023 12:20
Copy link
Contributor

@steventux steventux left a comment

Choose a reason for hiding this comment

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

Looks good to me. 👍
One non-blocking comment mirroring a point made on AYTQ,


redirect_to root_path
def check_user_access_to_service
Copy link
Contributor

Choose a reason for hiding this comment

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

@felixclack made a point on a related PR that renaming this method to role simplifies the code somewhat. Not a blocker.

@@ -1,7 +1,15 @@
class SignInController < ApplicationController
skip_before_action :authenticate_dsi_user!
skip_before_action :handle_expired_session!
before_action :reset_session
Copy link
Contributor

Choose a reason for hiding this comment

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

💯

Use memoization instead of local variable assignment.
@malcolmbaig malcolmbaig merged commit f2f5c75 into main Nov 9, 2023
10 checks passed
@malcolmbaig malcolmbaig deleted the 53-update-dsi-sign-out branch November 9, 2023 09:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants