diff --git a/.bacon.yml b/.bacon.yml index 85c385272..8e094c2b8 100644 --- a/.bacon.yml +++ b/.bacon.yml @@ -53,11 +53,18 @@ test_suites: criteria: MERGE queue_name: small - - name: sample-express-embedded-auth-with-sdk + - name: sample-express-embedded-auth-with-sdk-SPEC script_path: ../okta-auth-js/scripts/samples sort_order: '6' timeout: '30' - script_name: e2e-express-embedded-auth-with-sdk + script_name: e2e-express-embedded-auth-with-sdk-spec + criteria: MERGE + queue_name: small + - name: sample-express-embedded-auth-with-sdk-FEATURES + script_path: ../okta-auth-js/scripts/samples + sort_order: '6' + timeout: '30' + script_name: e2e-express-embedded-auth-with-sdk-features criteria: MERGE queue_name: small - name: sample-express-web-no-oidc diff --git a/CHANGELOG.md b/CHANGELOG.md index 81d77f0a3..2dc2f6cc9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## 7.4.2 + +### Bug Fix + +- [#1448](https://github.com/okta/okta-auth-js/pull/1448) Fix: UA string in Node no longer continuously extends + ## 7.4.1 ### Bug Fix diff --git a/lib/http/OktaUserAgent.ts b/lib/http/OktaUserAgent.ts index 7c0d29db1..8f39946a1 100644 --- a/lib/http/OktaUserAgent.ts +++ b/lib/http/OktaUserAgent.ts @@ -20,6 +20,7 @@ export class OktaUserAgent { constructor() { // add base sdk env this.environments = [`okta-auth-js/${SDK_VERSION}`]; + this.maybeAddNodeEnvironment(); } addEnvironment(env: string) { @@ -27,7 +28,6 @@ export class OktaUserAgent { } getHttpHeader() { - this.maybeAddNodeEnvironment(); return { 'X-Okta-User-Agent-Extended': this.environments.join(' ') }; } diff --git a/package.json b/package.json index f4396dc10..fba0f97a6 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "private": true, "name": "@okta/okta-auth-js", "description": "The Okta Auth SDK", - "version": "7.4.1", + "version": "7.4.2", "homepage": "https://github.com/okta/okta-auth-js", "license": "Apache-2.0", "main": "build/cjs/exports/default.js", diff --git a/samples/test/cucumber.wdio.conf.ts b/samples/test/cucumber.wdio.conf.ts index 6b495b8a2..ee3057326 100644 --- a/samples/test/cucumber.wdio.conf.ts +++ b/samples/test/cucumber.wdio.conf.ts @@ -157,7 +157,7 @@ export const config: WebdriverIO.Config = { // and 30 processes will get spawned. The property handles how many capabilities // from the same test should run tests. // - maxInstances: 10, + maxInstances: 2, // // If you have trouble getting all important capabilities together, check out the // Sauce Labs platform configurator - a great tool to configure your capabilities: diff --git a/samples/test/features/account-unlock.feature b/samples/test/features/account-unlock.feature index 8eccbf9be..c4ac5bc3a 100644 --- a/samples/test/features/account-unlock.feature +++ b/samples/test/features/account-unlock.feature @@ -38,6 +38,7 @@ Feature: Account Unlock with Single factor (Email, Phone, Okta Verify Push) When she clicks the Email magic link for unlock Then she should see a message containing "unlocked!" + @smstest Scenario: Mary recovers from a locked account with Phone SMS OTP Given she has enrolled in the "SMS" factor And Mary has entered an incorrect password to trigger an account lockout diff --git a/samples/test/features/mfa-password-and-sms.feature b/samples/test/features/mfa-password-and-sms.feature index 3eec44a2d..264a24bb8 100644 --- a/samples/test/features/mfa-password-and-sms.feature +++ b/samples/test/features/mfa-password-and-sms.feature @@ -11,6 +11,7 @@ Feature: Multi-Factor Authentication with Password and SMS And a user named "Mary" And she has an account with "active" state in the org + @smstest Scenario: Enroll in SMS Factor prompt when authenticating When she clicks the "login" button Then she is redirected to the "Login" page @@ -58,6 +59,7 @@ Feature: Multi-Factor Authentication with Password and SMS And she submits the form Then she should see a message "Invalid Phone Number." + @smstest Scenario: 2FA Login with SMS Given she has enrolled in the "SMS" factor When she clicks the "login" button diff --git a/samples/test/features/self-service-registration.feature b/samples/test/features/self-service-registration.feature index f628241be..1fdf7a635 100644 --- a/samples/test/features/self-service-registration.feature +++ b/samples/test/features/self-service-registration.feature @@ -39,6 +39,7 @@ Scenario: Mary signs up for an account with Password, setups up required Email f And she sees a table with her profile info And the cell for the value of "email" is shown and contains her "email" +@smstest Scenario: Mary signs up for an account with Password, setups up required Email factor, And sets up optional SMS When she clicks the 'signup' button Then she is redirected to the "Self Service Registration" page diff --git a/samples/test/features/totp-okta-verify-signin.feature b/samples/test/features/totp-okta-verify-signin.feature index dc457a523..df5a9a4c2 100644 --- a/samples/test/features/totp-okta-verify-signin.feature +++ b/samples/test/features/totp-okta-verify-signin.feature @@ -31,6 +31,7 @@ Feature: TOTP Support (Okta Verify) Sign In # And the cell for the value of "email" is shown and contains her "email" # And the cell for the value of "name" is shown and contains her "first name and last name" + @smstest Scenario: Mary signs in to an account and enrolls in Password and Okta Verify by clicking a link in a text message When she clicks the "login" button Then she is redirected to the "Login" page diff --git a/samples/test/features/totp-okta-verify-signup.feature b/samples/test/features/totp-okta-verify-signup.feature index 54c59675b..8a5719061 100644 --- a/samples/test/features/totp-okta-verify-signup.feature +++ b/samples/test/features/totp-okta-verify-signup.feature @@ -37,6 +37,7 @@ Feature: TOTP Support (Okta Verify) Sign Up # And the cell for the value of "email" is shown and contains her "email" # And the cell for the value of "name" is shown and contains her "first name and last name" + @smstest Scenario: Mary signs up for an account with Password, setups up required Okta Verify by clicking a link in a text message When she clicks the 'signup' button Then she is redirected to the "Self Service Registration" page diff --git a/samples/test/steps/before.ts b/samples/test/steps/before.ts index 9d6030cbc..41a682999 100644 --- a/samples/test/steps/before.ts +++ b/samples/test/steps/before.ts @@ -14,6 +14,12 @@ import { Before } from '@cucumber/cucumber'; import ActionContext from '../support/context'; +Before('@smstest', function () { + if (process.env.SKIP_SMS === 'true') { + return 'skipped'; + } +}); + Before(function (this: ActionContext, scenario: any) { this.featureName = scenario?.gherkinDocument?.feature?.name; this.scenarioName = scenario?.pickle?.name; diff --git a/scripts/samples/e2e-express-embedded-auth-with-sdk-features.sh b/scripts/samples/e2e-express-embedded-auth-with-sdk-features.sh new file mode 100644 index 000000000..c5c89c7ae --- /dev/null +++ b/scripts/samples/e2e-express-embedded-auth-with-sdk-features.sh @@ -0,0 +1,26 @@ +#!/bin/bash + +source $(dirname "${BASH_SOURCE[0]}")/../setup-e2e.sh + +# NOTE: this test suite runs against a separate test org on OK14 +export USE_OK_14=1 +setup_sample_tests + +export SAMPLE_NAME=@okta/samples.express-embedded-auth-with-sdk +export MAX_INSTANCES=2 + +# NOTE: the command below evaluates to the same PASSWORD retrieved in setup-e2e, leaving commented just in case +# get_vault_secret_key devex/prod-js-idx-sdk-vars password PASSWORD + +# based on run_sample_tests +create_log_group "E2E Test Run" + # Run the tests + if ! yarn workspace @okta/test.e2e.samples test:features; then + echo "tests failed! Exiting..." + exit ${TEST_FAILURE} + fi + + echo ${TEST_SUITE_TYPE} > ${TEST_SUITE_TYPE_FILE} + echo ${TEST_RESULT_FILE_DIR} > ${TEST_RESULT_FILE_DIR_FILE} + exit ${PUBLISH_TYPE_AND_RESULT_DIR} +finish_log_group $? \ No newline at end of file diff --git a/scripts/samples/e2e-express-embedded-auth-with-sdk-spec.sh b/scripts/samples/e2e-express-embedded-auth-with-sdk-spec.sh new file mode 100644 index 000000000..4bdfe4a7d --- /dev/null +++ b/scripts/samples/e2e-express-embedded-auth-with-sdk-spec.sh @@ -0,0 +1,26 @@ +#!/bin/bash + +source $(dirname "${BASH_SOURCE[0]}")/../setup-e2e.sh + +# NOTE: this test suite runs against a separate test org on OK14 +export USE_OK_14=1 +setup_sample_tests + +export SAMPLE_NAME=@okta/samples.express-embedded-auth-with-sdk +export MAX_INSTANCES=2 + +# NOTE: the command below evaluates to the same PASSWORD retrieved in setup-e2e, leaving commented just in case +# get_vault_secret_key devex/prod-js-idx-sdk-vars password PASSWORD + +# based on run_sample_tests +create_log_group "E2E Test Run" + # Run the tests + if ! yarn workspace @okta/test.e2e.samples test:specs; then + echo "tests failed! Exiting..." + exit ${TEST_FAILURE} + fi + + echo ${TEST_SUITE_TYPE} > ${TEST_SUITE_TYPE_FILE} + echo ${TEST_RESULT_FILE_DIR} > ${TEST_RESULT_FILE_DIR_FILE} + exit ${PUBLISH_TYPE_AND_RESULT_DIR} +finish_log_group $? diff --git a/test/e2e/cucumber.wdio.conf.ts b/test/e2e/cucumber.wdio.conf.ts index b5350af6b..6936c91d6 100644 --- a/test/e2e/cucumber.wdio.conf.ts +++ b/test/e2e/cucumber.wdio.conf.ts @@ -114,7 +114,7 @@ export const config: Options.Testrunner = { // and 30 processes will get spawned. The property handles how many capabilities // from the same test should run tests. // - maxInstances: 10, + maxInstances: 1, // // If you have trouble getting all important capabilities together, check out the // Sauce Labs platform configurator - a great tool to configure your capabilities: diff --git a/test/e2e/features/acr-values.feature b/test/e2e/features/acr-values.feature index c4c373a8f..bc650186a 100644 --- a/test/e2e/features/acr-values.feature +++ b/test/e2e/features/acr-values.feature @@ -29,6 +29,7 @@ Scenario: Mary logs in with the initial App Authentication Policy Then she sees the default view in an AUTHENTICATED state And she sees her ID and Access Tokens +@smstest Scenario: Mary logs in with an ACR value in the Authorize request Given Mary is on the default view in an UNAUTHENTICATED state When she selects "urn:okta:loa:2fa:any:ifpossible" into "ACR values" @@ -52,6 +53,7 @@ Scenario: Mary logs in with an ACR value in the Authorize request Then she sees the default view in an AUTHENTICATED state And she sees her ID and Access Tokens +@smstest Scenario: Mary is signed in without ACR, and is challenged with an ACR value # Authenticate using SIW Given Mary is on the default view in an UNAUTHENTICATED state @@ -85,6 +87,7 @@ Scenario: Mary is signed in without ACR, and is challenged with an ACR value Then she sees the default view in an AUTHENTICATED state And she sees her ID and Access Tokens +@smstest Scenario: Mary is signed in without ACR, and is challenged with an ACR value, but has a valid ACR Token # Authenticate using SIW with ACR values 'urn:okta:loa:2fa:any:ifpossible' Given Mary is on the default view in an UNAUTHENTICATED state diff --git a/test/e2e/features/step-definitions/before.ts b/test/e2e/features/step-definitions/before.ts index d21e1d888..6ff79b5d7 100644 --- a/test/e2e/features/step-definitions/before.ts +++ b/test/e2e/features/step-definitions/before.ts @@ -1,6 +1,12 @@ import { Before } from '@wdio/cucumber-framework'; import ActionContext from 'support/context'; +Before('@smstest', function () { + if (process.env.SKIP_SMS === 'true') { + return 'skipped'; + } +}); + Before(function (this: ActionContext, scenario: any) { this.featureName = scenario?.gherkinDocument?.feature?.name; this.scenarioName = scenario?.pickle?.name; diff --git a/test/spec/OktaUserAgent.ts b/test/spec/OktaUserAgent.ts index 84e27d3f9..023c81953 100644 --- a/test/spec/OktaUserAgent.ts +++ b/test/spec/OktaUserAgent.ts @@ -1,63 +1,117 @@ +const SDK_VERSION = (global as any).SDK_VERSION; +const NODE_VERSION = (global as any).NODE_VERSION; + import { OktaUserAgent } from '../../lib/http/OktaUserAgent'; -jest.mock('../../lib/features', () => { +const mocked = { + isBrowser: jest.fn() +}; +jest.mock('lib/features', () => { return { - isBrowser: () => {} + __esModule: true, + isBrowser: () => mocked.isBrowser() }; }); -const mocked = { - features: require('../../lib/features') -}; - describe('OktaUserAgent', () => { - let oktaUserAgent; - let sdkVersion; - beforeEach(async () => { - sdkVersion = (await import('../../package.json')).version; - oktaUserAgent = new OktaUserAgent(); + const context: any = {}; + + beforeEach(() => { + context.oktaUserAgent = new OktaUserAgent(); }); describe('browser env', () => { beforeEach(() => { - jest.spyOn(mocked.features, 'isBrowser').mockReturnValue(true); + mocked.isBrowser.mockReturnValue(true); + context.expected = `okta-auth-js/${SDK_VERSION}`; + context.oktaUserAgent = new OktaUserAgent(); }); + it('gets okta-auth-js and node info in the Okta UA by default', () => { + const { oktaUserAgent, expected } = context; const httpHeader = oktaUserAgent.getHttpHeader(); expect(httpHeader).toEqual({ - 'X-Okta-User-Agent-Extended': `okta-auth-js/${sdkVersion}` + 'X-Okta-User-Agent-Extended': expected }); }); + it('can add extra environment', () => { + const { oktaUserAgent, expected } = context; oktaUserAgent.addEnvironment('fake/x.y'); const httpHeader = oktaUserAgent.getHttpHeader(); expect(httpHeader).toEqual({ - 'X-Okta-User-Agent-Extended': `okta-auth-js/${sdkVersion} fake/x.y` + 'X-Okta-User-Agent-Extended': `${expected} fake/x.y` + }); + }); + + // Reason: OKTA-641280 + it('should return same header after multiple calls', () => { + const { oktaUserAgent, expected } = context; + expect(oktaUserAgent.getHttpHeader()).toEqual({ + 'X-Okta-User-Agent-Extended': expected + }); + expect(oktaUserAgent.getHttpHeader()).toEqual({ + 'X-Okta-User-Agent-Extended': expected + }); + expect(oktaUserAgent.getHttpHeader()).toEqual({ + 'X-Okta-User-Agent-Extended': expected + }); + expect(oktaUserAgent.getHttpHeader()).toEqual({ + 'X-Okta-User-Agent-Extended': expected + }); + expect(oktaUserAgent.getHttpHeader()).toEqual({ + 'X-Okta-User-Agent-Extended': expected }); }); }); describe('node env', () => { - let nodeVersion = process.versions.node; beforeEach(() => { - jest.spyOn(mocked.features, 'isBrowser').mockReturnValue(false); + mocked.isBrowser.mockReturnValue(false); + context.expected = `okta-auth-js/${SDK_VERSION} nodejs/${NODE_VERSION}`; + context.oktaUserAgent = new OktaUserAgent(); }); + it('gets okta-auth-js and node info in the Okta UA by default', () => { + const { oktaUserAgent, expected } = context; const httpHeader = oktaUserAgent.getHttpHeader(); expect(httpHeader).toEqual({ - 'X-Okta-User-Agent-Extended': `okta-auth-js/${sdkVersion} nodejs/${nodeVersion}` + 'X-Okta-User-Agent-Extended': expected }); }); + it('can add extra environment', () => { + const { oktaUserAgent, expected } = context; oktaUserAgent.addEnvironment('fake/x.y'); const httpHeader = oktaUserAgent.getHttpHeader(); expect(httpHeader).toEqual({ - 'X-Okta-User-Agent-Extended': `okta-auth-js/${sdkVersion} fake/x.y nodejs/${nodeVersion}` + 'X-Okta-User-Agent-Extended': `${expected} fake/x.y` + }); + }); + + // Reason: OKTA-641280 + it('should return same header after multiple calls', () => { + const { oktaUserAgent, expected } = context; + expect(oktaUserAgent.getHttpHeader()).toEqual({ + 'X-Okta-User-Agent-Extended': expected + }); + expect(oktaUserAgent.getHttpHeader()).toEqual({ + 'X-Okta-User-Agent-Extended': expected + }); + expect(oktaUserAgent.getHttpHeader()).toEqual({ + 'X-Okta-User-Agent-Extended': expected + }); + expect(oktaUserAgent.getHttpHeader()).toEqual({ + 'X-Okta-User-Agent-Extended': expected + }); + expect(oktaUserAgent.getHttpHeader()).toEqual({ + 'X-Okta-User-Agent-Extended': expected }); }); }); it('can get sdk version', () => { - expect(oktaUserAgent.getVersion()).toBe(sdkVersion); + const { oktaUserAgent } = context; + expect(oktaUserAgent.getVersion()).toBe(SDK_VERSION); }); }); diff --git a/test/support/jest/jest.config.js b/test/support/jest/jest.config.js index 4e850cd39..b371a5e30 100644 --- a/test/support/jest/jest.config.js +++ b/test/support/jest/jest.config.js @@ -14,12 +14,19 @@ var OktaAuth = '/lib/exports/default'; var SDK_VERSION = require('../../../package.json').version; +const { node: NODE_VERSION } = process.versions; + module.exports = { 'coverageDirectory': '/build2/reports/coverage-browser', 'collectCoverage': true, 'collectCoverageFrom': ['./lib/**','!./test/**'], 'globals': { - SDK_VERSION + SDK_VERSION, + NODE_VERSION, + 'ts-jest': { + SDK_VERSION, + NODE_VERSION + } }, 'transform': { '^.+\\.(js)$': 'babel-jest',