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

Okta Identity Engine | Switch from sid cookie to idx cookie in tests and documentation #2450

Merged
merged 1 commit into from
Oct 23, 2023
Merged
Show file tree
Hide file tree
Changes from all 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
2 changes: 2 additions & 0 deletions cypress/integration/ete-okta/jobs_terms.4.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ describe('Jobs terms and conditions flow in Okta', () => {
'BASE_URI',
)}/agree/GRS?returnUrl=https://profile.thegulocal.com/signin?returnUrl=https%3A%2F%2Fm.code.dev-theguardian.com%2F`;
cy.setCookie('sid', 'invalid-cookie');
cy.setCookie('idx', 'invalid-cookie');
cy.visit(termsAcceptPageUrl);
cy.url().should(
'include',
Expand Down Expand Up @@ -175,6 +176,7 @@ describe('Jobs terms and conditions flow in Okta', () => {
cy.url().should('include', `/agree/GRS`);
// check session cookie is set
cy.getCookie('sid').should('exist');
cy.getCookie('idx').should('exist');
// check idapi cookies are set
cy.getCookie('SC_GU_U').should('exist');
cy.getCookie('SC_GU_LA').should('exist');
Expand Down
20 changes: 10 additions & 10 deletions cypress/integration/ete-okta/reauthenticate.4.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,11 @@ describe('Reauthenticate flow, Okta enabled', () => {
cy.url().should('include', '/consents/data');

// Get the current session data
cy.getCookie('sid').then((sidCookie) => {
const sid = sidCookie?.value;
expect(sid).to.exist;
if (sid) {
cy.getCurrentOktaSession({ sid }).then((session) => {
cy.getCookie('idx').then((idxCookie) => {
const idx = idxCookie?.value;
expect(idx).to.exist;
if (idx) {
cy.getCurrentOktaSession({ idx }).then((session) => {
expect(session.login).to.equal(emailAddress);
});
}
Expand Down Expand Up @@ -83,11 +83,11 @@ describe('Reauthenticate flow, Okta enabled', () => {
cy.url().should('include', '/consents/data');

// Get the current session data
cy.getCookie('sid').then((sidCookie) => {
const sid = sidCookie?.value;
expect(sid).to.exist;
if (sid) {
cy.getCurrentOktaSession({ sid }).then((session) => {
cy.getCookie('idx').then((idxCookie) => {
const idx = idxCookie?.value;
expect(idx).to.exist;
if (idx) {
cy.getCurrentOktaSession({ idx }).then((session) => {
expect(session.login).to.equal(emailAddressB);
});
}
Expand Down
4 changes: 2 additions & 2 deletions cypress/integration/ete-okta/registration_2.6.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -617,8 +617,8 @@ describe('Registration flow - Split 2/2', () => {
cy.url().should('include', '/consents');

// Get the current session data
cy.getCookie('sid').then((originalSidCookie) => {
expect(originalSidCookie).to.exist;
cy.getCookie('idx').then((originalIdxCookie) => {
expect(originalIdxCookie).to.exist;

// Visit register again
cy.visit(
Expand Down
23 changes: 15 additions & 8 deletions cypress/integration/ete-okta/sign_in.1.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ describe('Sign in flow, Okta enabled', () => {
// Get the refreshed session data
cy.getCookie('idx').then((newIdxCookie) => {
expect(newIdxCookie).to.exist;
// `idx` cookie doesn't have same value as original when refreshed, which is different to the Okta Classic `sid` cookie
// `idx` cookie doesn't have same value as original when refreshed, which is different to the Okta Classic `idx` cookie
expect(newIdxCookie?.value).to.not.equal(orignalIdxCookie?.value);
// we want to check the cookie is being set as a persistent cookie and not a session cookie, hence the expiry check
expect(newIdxCookie?.expiry).to.exist;
Expand Down Expand Up @@ -162,10 +162,10 @@ describe('Sign in flow, Okta enabled', () => {
cy.url().should('include', '/consents');

// Get the current session data
cy.getCookie('sid').then((sidCookie) => {
cy.getCookie('idx').then((idxCookie) => {
// Close the user's current session in Okta
cy.closeCurrentOktaSession({
sid: sidCookie?.value,
idx: idxCookie?.value,
}).then(() => {
// Refresh our user session
cy.visit(
Expand Down Expand Up @@ -194,7 +194,7 @@ describe('Sign in flow, Okta enabled', () => {
cy.get('[data-cy="main-form-submit-button"]').click();
cy.url().should('include', '/consents');

// Delete the Okta sid cookie
// Delete the Okta sid/idx cookie
// strange behaviour form Cypress 12
// where we need to delete cookie from both domains
// to get the test to pass
Expand All @@ -206,6 +206,12 @@ describe('Sign in flow, Okta enabled', () => {
cy.clearCookie('sid', {
domain: `.${Cypress.env('BASE_URI')}`,
});
cy.clearCookie('idx', {
domain: Cypress.env('BASE_URI'),
});
cy.clearCookie('idx', {
domain: `.${Cypress.env('BASE_URI')}`,
});

// Visit the refresh endpoint
cy.visit(
Expand All @@ -216,6 +222,7 @@ describe('Sign in flow, Okta enabled', () => {
cy.url().should('include', '/reset-password');

cy.getCookie('sid').should('not.exist');
cy.getCookie('idx').should('not.exist');
},
);
});
Expand Down Expand Up @@ -334,8 +341,8 @@ describe('Sign in flow, Okta enabled', () => {
cy.contains('Change email address');

// Ensure the user's authentication cookies are not set
cy.getCookie('sid').then((sidCookie) => {
expect(sidCookie).to.not.exist;
cy.getCookie('idx').then((idxCookie) => {
expect(idxCookie).to.not.exist;

cy.checkForEmailAndGetDetails(
emailAddress,
Expand Down Expand Up @@ -387,8 +394,8 @@ describe('Sign in flow, Okta enabled', () => {
cy.url().should('include', '/consents');

// Get the current session data
cy.getCookie('sid').then((originalSidCookie) => {
expect(originalSidCookie).to.exist;
cy.getCookie('idx').then((originalIdxCookie) => {
expect(originalIdxCookie).to.exist;

// Visit sign in again
cy.visit(
Expand Down
1 change: 1 addition & 0 deletions cypress/integration/ete-okta/sign_out.5.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ describe('Sign out flow', () => {
cy.url().should('include', `/consents/data`);
// check session cookie is set
cy.getCookie('sid').should('exist');
cy.getCookie('idx').should('exist');
// check idapi cookies are set
cy.getCookie('SC_GU_U').should('exist');
cy.getCookie('SC_GU_LA').should('exist');
Expand Down
24 changes: 12 additions & 12 deletions cypress/integration/mocked/okta_register.3.cy.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
describe('Okta Register flow', () => {
const setSidCookie = () => {
cy.setCookie('sid', `the_sid_cookie`, {
const setIdxCookie = () => {
cy.setCookie('idx', `the_idx_cookie`, {
domain: Cypress.env('BASE_URI'),
});
};
Expand All @@ -14,7 +14,7 @@ describe('Okta Register flow', () => {
cy.disableCMP();
});

it('should redirect to homepage if the sid Okta session cookie is valid', () => {
it('should redirect to homepage if the idx Okta session cookie is valid', () => {
cy.mockPattern(
200,
{
Expand All @@ -40,7 +40,7 @@ describe('Okta Register flow', () => {

cy.visit('/register');

setSidCookie();
setIdxCookie();

cy.get('input[name="email"]').type('[email protected]');
cy.mockNext(200, {
Expand Down Expand Up @@ -71,12 +71,12 @@ describe('Okta Register flow', () => {
cy.contains('Sign in with a different email');
});

it('should redirect to /reauthenticate if the sid Okta session cookie is set, but invalid', () => {
it('should redirect to /reauthenticate if the idx Okta session cookie is set, but invalid', () => {
cy.mockPattern(404, '/api/v1/sessions/me');

cy.mockPattern(204, {}, '/api/v1/users/userId/sessions');

setSidCookie();
setIdxCookie();

// visit healthcheck to set the cookie
cy.visit('/healthcheck');
Expand All @@ -85,15 +85,15 @@ describe('Okta Register flow', () => {

cy.location('pathname').should('eq', '/reauthenticate');

cy.getCookie('sid').should('not.exist');
cy.getCookie('idx').should('not.exist');
});
});

context('Signed in user visits /register', () => {
beforeEach(() => {
cy.mockPurge();
});
it('should redirect to homepage if the sid Okta session cookie is valid', () => {
it('should redirect to homepage if the idx Okta session cookie is valid', () => {
cy.mockPattern(
200,
{
Expand All @@ -115,7 +115,7 @@ describe('Okta Register flow', () => {

cy.mockPattern(204, {}, '/api/v1/users/userId/sessions');

setSidCookie();
setIdxCookie();

// disable the cmp on the redirect
cy.disableCMP();
Expand All @@ -138,12 +138,12 @@ describe('Okta Register flow', () => {
cy.contains('Sign in with a different email');
});

it('should redirect to /reauthenticate if the sid Okta session cookie is set but invalid', () => {
it('should redirect to /reauthenticate if the idx Okta session cookie is set but invalid', () => {
cy.mockPattern(404, '/api/v1/sessions/me');

cy.mockPattern(204, {}, '/api/v1/users/userId/sessions');

setSidCookie();
setIdxCookie();

// visit healthcheck to set the cookie
cy.visit('/healthcheck');
Expand All @@ -152,7 +152,7 @@ describe('Okta Register flow', () => {

cy.location('pathname').should('eq', '/reauthenticate');

cy.getCookie('sid').should('not.exist');
cy.getCookie('idx').should('not.exist');
});
});
});
4 changes: 2 additions & 2 deletions cypress/integration/mocked/okta_sign_in.6.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ describe('Sign in flow', () => {

cy.mockPattern(204, {}, '/api/v1/users/userId/sessions');

cy.setCookie('sid', `the_sid_cookie`);
cy.setCookie('idx', `the_idx_cookie`);

// disable the cmp on the redirect
cy.disableCMP();
Expand Down Expand Up @@ -85,7 +85,7 @@ describe('Sign in flow', () => {
'/api/v1/apps/123',
);

cy.setCookie('sid', `the_sid_cookie`);
cy.setCookie('idx', `the_idx_cookie`);

// disable the cmp on the redirect
cy.disableCMP();
Expand Down
2 changes: 1 addition & 1 deletion docs/okta/sessions.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ There are two layers of session in Okta in the following hierarchy.

We generally call this the Global Session or Okta session internally, but is known in the Okta documentation as the IdP Session.

The IdP session is the session that is created when a user logs in to or authenticates with Okta with their credentials and any various MFA options. This session is created by Okta and is identified by a cookie named `sid` in the browser.
The IdP session is the session that is created when a user logs in to or authenticates with Okta with their credentials and any various MFA options. This session is created by Okta and is identified by a cookie named `idx` in the browser, and `sid` cookie may also be set, but this is a legacy Okta identification cookie.

This cookie is only valid on the Okta/login domain (at the Guardian this is the `profile` subdomain, e.g. https://profile.theguardian.com), and the value is the session id. This same session id which can be used by the [Okta Sessions API](https://developer.okta.com/docs/reference/api/sessions/). This is an opaque value, and only authenticates the user, it does not contain any information about the user, and should not be used to authenticate the user on any API.

Expand Down
10 changes: 5 additions & 5 deletions docs/okta/signin.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ Throughout the implementation of the sign in code, there are many in line commen
In general the steps of sign in with email and password are summarised as follows, assuming okta is enabled, this does not detail all technical requirements, just the main high level:

- User navigates to `/signin`
- Check `sid` cookie for existing Okta session, if this is present it checks if the session is still valid.
- Check `idx` cookie for existing Okta session, if this is present it checks if the session is still valid.
- If the current session does not exist, or is invalid, sign in page shown
- user enters email and password, makes request to gateway `POST /signin`
- Use okta authenticate endpoint with the email and password `/api/v1/authn`
Expand All @@ -41,7 +41,7 @@ sequenceDiagram
participant Okta
participant Identity API
Browser->>Gateway: Request gateway /signin
note over Gateway: Sign in session is checked by inspecting the `sid` cookie
note over Gateway: Sign in session is checked by inspecting the `idx` cookie
Gateway->>Okta: GET /api/v1/sessions/:sessionId
Okta->>Gateway: return invalid session response
Gateway->>Browser: render sign in page
Expand Down Expand Up @@ -74,7 +74,7 @@ sequenceDiagram
participant Okta
participant Identity API
Browser->>Gateway: Request gateway /signin
note over Gateway: Sign in session is checked by inspecting the `sid` cookie and validating the session against the Okta session API
note over Gateway: Sign in session is checked by inspecting the `idx` cookie and validating the session against the Okta session API
note over Okta: check for existing session<br>in this case a session exists
Gateway->>Okta: GET /api/v1/sessions/:sessionId
Okta->>Gateway: return valid session response
Expand Down Expand Up @@ -103,7 +103,7 @@ participant Social
participant Identity API

Browser ->> Gateway: Request /signin (or /register),<br>will just be referred to as /signin or sign in for<br>simplicity, as the same thing happens<br>in either case
note over Gateway: Sign in session is checked by inspecting the `sid` cookie
note over Gateway: Sign in session is checked by inspecting the `idx` cookie
Gateway ->> Okta: GET /api/v1/sessions/:sessionId
Okta ->> Gateway: Return invalid session response
Gateway ->> Browser: Render sign in page
Expand Down Expand Up @@ -156,7 +156,7 @@ participant Social
participant Identity API

Browser ->> Gateway: Request /signin (or /register),<br>will just be referred to as /signin or sign in for<br>simplicity, as the same thing happens<br>in either case
note over Gateway: Sign in session is checked by inspecting the `sid` cookie
note over Gateway: Sign in session is checked by inspecting the `idx` cookie
Gateway ->> Okta: GET /api/v1/sessions/:sessionId
Okta ->> Gateway: Return invalid session response
Gateway ->> Browser: Render sign in page
Expand Down
14 changes: 7 additions & 7 deletions docs/okta/signout.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
# Sign Out with Okta

Status: RFC

This document describes how we've implemented the sign out flow with Okta in Gateway.

In old (current) Identity land, when a user clicks the "sign out" button on the website, or visit the sign out link directly, if they have a valid session, determined using the `SC_GU_U` cookie, Identity API will invalidate all existing user sessions on all devices and browsers, and clear all Identity and related dotcom cookies on the current browser.
Expand All @@ -12,15 +10,17 @@ Okta provides an administrative API endpoint within the Users API to clear all u

https://developer.okta.com/docs/reference/api/users/#clear-user-sessions

This endpoint requires the okta `userId`, which we get from the okta sessions api using the current okta session id cookie `sid`
This endpoint requires the okta `userId`, which we get from the okta sessions api using the current okta session id cookie `idx`

We then use this user id to clear all Okta sessions for that user.

In the sceario where the okta call to remove the fails (and we have a orphaned session), we delete the sid cookie anyway and we plan to allow the users to see their active sessions in manage my account section of the site where they can remove them manually.
In the scenario where the okta call to remove the session fails (and we have a orphaned session), we delete the `idx` cookie anyway, and we plan in the future to allow the users to see their active sessions in manage my account section of the site where they can remove them manually.

If there is no existing session then we'll render the sign in page.

During the dual running of the system we also need to clear the Identity session too. We can use this access token or the SC_GU_U cookie to call Identity API (TBD which option) to do this.
During the dual running of the system we also need to clear the Identity session too, we do this by calling the `/unauth` endpoint on the Identity API, which will clear the IDAPI session using the`SC_GU_U` cookie.

We then set the `GU_SO` which identifies that the user has signed out recently.

### User has existing session

Expand All @@ -36,7 +36,7 @@ sequenceDiagram
Gateway->>Okta: Clear user sessions<br/>using userId<br/>DELETE /api/v1/users/${userId}/sessions?oauthTokens=true
Okta->>Gateway: Return 204 No Content
opt Clear Identity Session (dual running)
Gateway->>Identity API: POST /unauth with SC_GU_U cookie
Gateway->>Identity API: POST /unauth with `SC_GU_U` cookie
Identity API->>Gateway: Return GU_SO cookie
end
Gateway->>Browser: Redirect to returnUrl<br/>clear identity cookies
Expand All @@ -50,7 +50,7 @@ sequenceDiagram
participant Gateway
participant Okta
Browser->>Gateway: GET /signout?returnUrl=...
note over Gateway: Check the presence of the SC_GU_U cookie and sid cookie, <br/>if neither are present they are not logged in
note over Gateway: Check the presence of the `SC_GU_U` cookie and `idx` cookie, <br/>if neither are present they are not logged in
Gateway->>Browser: Redirect request to gateway `/signin`
Browser->>Gateway: Request gateway /signin
note over Gateway: Sign in session is checked<br/>(encryptedState.signInRedirect == true)<br>remove/set to false from the encryptedState
Expand Down
2 changes: 1 addition & 1 deletion docs/okta/tokens.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ However, public clients such as browser-based applications have a much higher ri

Refresh tokens are valid for 90 days by default, but can be configured to be longer or shorter, from 5 mins to unlimited, we also recommend that you rotate the refresh token after each use.

Refresh tokens provide an additional "session" layer on top of the in browser `sid` cookie (which is set after login in browser). Meaning these two sessions are independent of each other, and can be used to manage the user's session in different ways. You can see the [Sessions](sessions.md) documentation for more information on how we use these sessions.
Refresh tokens provide an additional "session" layer on top of the in browser `idx` cookie (which is set after login in browser). Meaning these two sessions are independent of each other, and can be used to manage the user's session in different ways. You can see the [Sessions](sessions.md) documentation for more information on how we use these sessions.

### Token management

Expand Down
Loading
Loading