diff --git a/addons/api/addon/generated/models/auth-method.js b/addons/api/addon/generated/models/auth-method.js index 91cbe70d0..0a268a422 100644 --- a/addons/api/addon/generated/models/auth-method.js +++ b/addons/api/addon/generated/models/auth-method.js @@ -158,6 +158,13 @@ export default class GeneratedAuthMethodModel extends BaseModel { }) account_claim_maps; + @attr('string-array', { + for: 'oidc', + emptyArrayIfMissing: true, + isNestedAttribute: true, + }) + prompts; + // =attributes(ldap) @attr('boolean', { diff --git a/addons/api/addon/models/auth-method.js b/addons/api/addon/models/auth-method.js index d62a93a45..be6edf06c 100644 --- a/addons/api/addon/models/auth-method.js +++ b/addons/api/addon/models/auth-method.js @@ -37,6 +37,7 @@ export const options = { account_claim_maps: { to: ['sub', 'name', 'email'], }, + prompts: ['none', 'consent', 'select_account', 'login'], }, ldap: { account_attribute_maps: { diff --git a/addons/api/mirage/generated/factories/auth-method.js b/addons/api/mirage/generated/factories/auth-method.js index 2336949df..63cdc3b9f 100644 --- a/addons/api/mirage/generated/factories/auth-method.js +++ b/addons/api/mirage/generated/factories/auth-method.js @@ -42,6 +42,7 @@ export default Factory.extend({ signing_algorithms: ['RS256', 'RS384'], allowed_audiences: ['www.alice.com', 'www.alice.com/admin'], idp_ca_certs: ['certificate-1234', 'certificate-5678'], + prompts: ['consent'], }; break; case TYPE_AUTH_METHOD_LDAP: diff --git a/addons/api/tests/unit/serializers/application-test.js b/addons/api/tests/unit/serializers/application-test.js index c283f5cfe..81964786f 100644 --- a/addons/api/tests/unit/serializers/application-test.js +++ b/addons/api/tests/unit/serializers/application-test.js @@ -165,6 +165,7 @@ module('Unit | Serializer | application', function (hooks) { dry_run: true, issuer: 'http://www.example.net', max_age: 500, + prompts: [{ value: 'consent' }], }); let serializedRecord = record.serialize(); @@ -186,6 +187,7 @@ module('Unit | Serializer | application', function (hooks) { dry_run: true, issuer: 'http://www.example.net', max_age: 500, + prompts: ['consent'], }, }); }); diff --git a/addons/api/tests/unit/serializers/auth-method-test.js b/addons/api/tests/unit/serializers/auth-method-test.js index eaeab8164..d23abd471 100644 --- a/addons/api/tests/unit/serializers/auth-method-test.js +++ b/addons/api/tests/unit/serializers/auth-method-test.js @@ -65,6 +65,7 @@ module('Unit | Serializer | auth method', function (hooks) { dry_run: true, issuer: 'http://www.example.net', max_age: 500, + prompts: [{ value: 'none' }], }); let serializedRecord = record.serialize(); @@ -86,6 +87,7 @@ module('Unit | Serializer | auth method', function (hooks) { dry_run: true, issuer: 'http://www.example.net', max_age: 500, + prompts: ['none'], }, }); }); @@ -180,6 +182,7 @@ module('Unit | Serializer | auth method', function (hooks) { dry_run: true, issuer: 'http://www.example.net', max_age: 500, + prompts: [{ value: 'consent' }], }); let serializedRecord = record.serialize(); @@ -200,6 +203,7 @@ module('Unit | Serializer | auth method', function (hooks) { dry_run: true, issuer: 'http://www.example.net', max_age: 500, + prompts: ['consent'], }, }); }); @@ -397,6 +401,7 @@ module('Unit | Serializer | auth method', function (hooks) { issuer: issuer, max_age: maxAge, client_secret: 'secret123', + prompts: ['consent'], }, version: 1, type: TYPE_AUTH_METHOD_OIDC, @@ -410,6 +415,7 @@ module('Unit | Serializer | auth method', function (hooks) { allowed_audiences, signing_algorithms, idp_ca_certs, + prompts, } = record; assert.deepEqual(account_claim_maps, [ { from: 'from', to: 'to' }, @@ -429,6 +435,7 @@ module('Unit | Serializer | auth method', function (hooks) { { value: 'certificate-5678' }, ]); assert.strictEqual(record.api_url_prefix, apiUrlPrefix); + assert.deepEqual(prompts, [{ value: 'consent' }]); assert.strictEqual(record.client_id, clientId); assert.strictEqual( record.disable_discovered_config_validation, diff --git a/addons/core/translations/form/en-us.yaml b/addons/core/translations/form/en-us.yaml index 80edccdf8..84cf1bb38 100644 --- a/addons/core/translations/form/en-us.yaml +++ b/addons/core/translations/form/en-us.yaml @@ -119,6 +119,9 @@ to_claim: signing_algorithms: label: Signing Algorithms help: The signing algorithms allowed for the auth method. +prompts: + label: Prompts + help: The prompts allowed for an oidc auth method. algorithm: label: Algorithm subject: diff --git a/addons/core/translations/resources/en-us.yaml b/addons/core/translations/resources/en-us.yaml index 7f7ab560b..2ea9cb06d 100644 --- a/addons/core/translations/resources/en-us.yaml +++ b/addons/core/translations/resources/en-us.yaml @@ -147,6 +147,18 @@ auth-method: account_attribute_maps: label: Account Attribute Maps help: Attribute maps from custom attributes to the standard fullname and email account attributes. These maps are represented as key=value where the key equals the from_attribute, and the value equals the to_attribute. For example, "preferredName=fullName". + consent: + label: Consent + help: Always require the user to explicitly consent to scopes requested + select_account: + label: Select Account + help: Allow the user to select from multiple accounts from this provider + login: + label: Login + help: Always require the user to log in to this auth provider + none: + label: None + help: Skip prompts if the user is already signed in and has granted consent section: connection: title: Connection diff --git a/ui/admin/app/components/form/auth-method/oidc/index.hbs b/ui/admin/app/components/form/auth-method/oidc/index.hbs index 4f03d37d2..5a37a27fb 100644 --- a/ui/admin/app/components/form/auth-method/oidc/index.hbs +++ b/ui/admin/app/components/form/auth-method/oidc/index.hbs @@ -528,6 +528,34 @@ {{/if}} + + {{t 'form.prompts.label'}} + {{t 'form.prompts.help'}} + + {{#each this.prompts as |prompt|}} + + + {{t (concat 'resources.auth-method.form.' prompt '.label')}} + + + {{t (concat 'resources.auth-method.form.' prompt '.help')}} + + + {{/each}} + + + {{#unless @model.isNew}} {{/unless}} + {{#if (can 'save model' @model)}} item.value) ?? []; + } + + //actions + + /** + * @param {string} option + * @param {object} event + */ + @action + updatePrompt(value, event) { + const currentSelection = { value }; + const previousSelection = this.args.model.prompts || []; + + // Add the selected option to the model, only when the checkbox is in checked state + if (event.target.checked) { + this.args.model.prompts = [...previousSelection, currentSelection]; + } else { + // When the checkbox is unchecked, remove the option form the exisiting list + const removeSelection = previousSelection.filter( + (prompt) => prompt.value !== value, + ); + this.args.model.prompts = [...removeSelection]; + } + } } diff --git a/ui/admin/app/styles/app.scss b/ui/admin/app/styles/app.scss index ad24acf48..3e38a4bf2 100644 --- a/ui/admin/app/styles/app.scss +++ b/ui/admin/app/styles/app.scss @@ -317,6 +317,14 @@ } } +/* stylelint-disable selector-class-pattern */ +.oidc-prompts { + .hds-form-group__control-field { + margin-bottom: 0.5rem; + } +} +/* stylelint-enable selector-class-pattern */ + .worker-create-section { border: sizing.rems(xxxxs) solid var(--ui-gray-subtler-3); border-radius: sizing.rems(xxs); diff --git a/ui/admin/tests/acceptance/auth-methods/create-test.js b/ui/admin/tests/acceptance/auth-methods/create-test.js index cfd09fbab..f035c4784 100644 --- a/ui/admin/tests/acceptance/auth-methods/create-test.js +++ b/ui/admin/tests/acceptance/auth-methods/create-test.js @@ -108,7 +108,7 @@ module('Acceptance | auth-methods | create', function (hooks) { }); test('Users can create new oidc auth method', async function (assert) { - assert.expect(12); + assert.expect(13); const count = getAuthMethodsCount(); await visit(`${urls.authMethods}/new?type=oidc`); @@ -134,6 +134,7 @@ module('Acceptance | auth-methods | create', function (hooks) { await click(IDP_CERTS_BTN_SELECTOR); await fillIn('[name="max_age"]', '5'); await fillIn('[name="api_url_prefix"]', 'api_url_prefix'); + await click('[id="consent"]', 'consent'); await click(SAVE_BTN_SELECTOR); assert.strictEqual(getAuthMethodsCount(), count + 1); @@ -153,6 +154,7 @@ module('Acceptance | auth-methods | create', function (hooks) { assert.deepEqual(authMethod.attributes.idp_ca_certs, ['certificates']); assert.strictEqual(authMethod.attributes.max_age, 5); assert.strictEqual(authMethod.attributes.api_url_prefix, 'api_url_prefix'); + assert.deepEqual(authMethod.attributes.prompts, ['consent']); }); test('Users can create a new ldap auth method', async function (assert) { diff --git a/ui/admin/tests/acceptance/auth-methods/update-test.js b/ui/admin/tests/acceptance/auth-methods/update-test.js index 22677afae..699b1b91e 100644 --- a/ui/admin/tests/acceptance/auth-methods/update-test.js +++ b/ui/admin/tests/acceptance/auth-methods/update-test.js @@ -108,7 +108,7 @@ module('Acceptance | auth-methods | update', function (hooks) { }); test('can update an oidc auth method and save changes', async function (assert) { - assert.expect(11); + assert.expect(12); instances.authMethod = this.server.create('auth-method', { scope: instances.scopes.org, type: TYPE_AUTH_METHOD_OIDC, @@ -173,6 +173,7 @@ module('Acceptance | auth-methods | update', function (hooks) { await click(IDP_CERTS_BTN_SELECTOR); await fillIn('[name="max_age"]', '5'); await fillIn('[name="api_url_prefix"]', 'api_url_prefix'); + await click('[id="none"]', 'none'); await click('form [type="submit"]:not(:disabled)'); const authMethod = this.server.schema.authMethods.findBy({ name }); @@ -191,6 +192,7 @@ module('Acceptance | auth-methods | update', function (hooks) { assert.deepEqual(authMethod.attributes.idp_ca_certs, ['certificates']); assert.strictEqual(authMethod.attributes.max_age, 5); assert.strictEqual(authMethod.attributes.api_url_prefix, 'api_url_prefix'); + assert.deepEqual(authMethod.attributes.prompts, ['consent', 'none']); }); test('can update an ldap auth method and save changes', async function (assert) {