-
Notifications
You must be signed in to change notification settings - Fork 4.6k
UI/OIDC provider #12800
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
Merged
Merged
UI/OIDC provider #12800
Changes from 13 commits
Commits
Show all changes
21 commits
Select commit
Hold shift + click to select a range
d576395
Add new route w/ controller oidc-provider
hashishaw efb4aeb
oidc-provider controller has params, template has success message (te…
hashishaw fb340e2
Move oidc-provider route to under identity
hashishaw 3f38ad2
Do not redirect after poll if on oidc-provider page
hashishaw 36a2366
WIP provider -- beforeModel handles prompt, logout, redirect
hashishaw 50ba8cf
Auth service fetch method rejects with fetch response if status >= 300
hashishaw fb97e47
New component OidcConsentBlock
hashishaw bfc9c4b
Fix redirect to/from auth with cluster name, show error and consent f…
hashishaw 6f4b3c3
Show error and consent form on template
hashishaw 0f7f420
Add component test, update docs
hashishaw 1080fba
Test for oidc-consent-block component
hashishaw b335a3c
Add changelog
hashishaw eb484be
fix tests
hashishaw b86216a
Add authorize to end of router path
hashishaw d87db7b
Remove unused tests
hashishaw e360231
Update changelog with feature name
hashishaw 4c73c5c
Add descriptions for OidcConsentBlock component
hashishaw 2d96955
glimmerize token-expire-warning and don't override yield if on oidc-p…
hashishaw 17f2d60
remove text on token-expire-warning
hashishaw 9582713
Fix null transition.to on cluster redirect
hashishaw f8f5591
Hide nav links if oidc-provider route
hashishaw File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| ```release-note:feature | ||
| ui: OIDC Authorization Code Flow Support | ||
| ``` | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,59 @@ | ||
| /** | ||
| * @module OidcConsentBlock | ||
| * OidcConsentBlock components are used to... | ||
|
||
| * | ||
| * @example | ||
| * ```js | ||
| * <OidcConsentBlock @requiredParam={requiredParam} @optionalParam={optionalParam} @param1={{param1}}/> | ||
| * ``` | ||
| * @param {string} redirect - redirect is the URL where successful consent will redirect to | ||
| * @param {string} code - code is the string required to pass back to redirect on successful OIDC auth | ||
| * @param {string} [state] - state is a string which is required to return on redirect if provided, but optional generally | ||
| */ | ||
|
|
||
| import Ember from 'ember'; | ||
| import Component from '@glimmer/component'; | ||
| import { action } from '@ember/object'; | ||
| import { tracked } from '@glimmer/tracking'; | ||
|
|
||
| const validParameters = ['code', 'state']; | ||
| export default class OidcConsentBlockComponent extends Component { | ||
| @tracked didCancel = false; | ||
|
|
||
| get win() { | ||
| return this.window || window; | ||
| } | ||
|
|
||
| buildUrl(urlString, params) { | ||
| try { | ||
| let url = new URL(urlString); | ||
| Object.keys(params).forEach(key => { | ||
| if (params[key] && validParameters.includes(key)) { | ||
| url.searchParams.append(key, params[key]); | ||
| } | ||
| }); | ||
| return url; | ||
| } catch (e) { | ||
| console.debug('DEBUG: parsing url failed for', urlString); | ||
| throw new Error('Invalid URL'); | ||
| } | ||
| } | ||
|
|
||
| @action | ||
| handleSubmit(evt) { | ||
| evt.preventDefault(); | ||
| let { redirect, ...params } = this.args; | ||
| let redirectUrl = this.buildUrl(redirect, params); | ||
| if (Ember.testing) { | ||
| this.args.testRedirect(redirectUrl.toString()); | ||
| } else { | ||
| this.win.location.replace(redirectUrl); | ||
| } | ||
| } | ||
|
|
||
| @action | ||
| handleCancel(evt) { | ||
| evt.preventDefault(); | ||
| this.didCancel = true; | ||
| } | ||
| } | ||
24 changes: 24 additions & 0 deletions
24
ui/app/controllers/vault/cluster/identity/oidc-provider.js
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| import Controller from '@ember/controller'; | ||
|
|
||
| export default class VaultClusterIdentityOidcProviderController extends Controller { | ||
| queryParams = [ | ||
| 'scope', // * | ||
| 'response_type', // * | ||
| 'client_id', // * | ||
| 'redirect_uri', // * | ||
| 'state', // * | ||
| 'nonce', // * | ||
| 'display', | ||
| 'prompt', | ||
| 'max_age', | ||
| ]; | ||
| scope = null; | ||
| response_type = null; | ||
| client_id = null; | ||
| redirect_uri = null; | ||
| state = null; | ||
| nonce = null; | ||
| display = null; | ||
| prompt = null; | ||
| max_age = null; | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,115 @@ | ||
| import Route from '@ember/routing/route'; | ||
| import { inject as service } from '@ember/service'; | ||
|
|
||
| const AUTH = 'vault.cluster.auth'; | ||
| const PROVIDER = 'vault.cluster.identity.oidc-provider'; | ||
|
|
||
| export default class VaultClusterIdentityOidcProviderRoute extends Route { | ||
| @service auth; | ||
| @service router; | ||
|
|
||
| get win() { | ||
| return this.window || window; | ||
| } | ||
|
|
||
| _redirect(url, params) { | ||
| let redir = this._buildUrl(url, params); | ||
| this.win.location.replace(redir); | ||
| } | ||
|
|
||
| beforeModel(transition) { | ||
| const currentToken = this.auth.get('currentTokenName'); | ||
| let { redirect_to, ...qp } = transition.to.queryParams; | ||
| console.debug('DEBUG: removing redirect_to', redirect_to); | ||
| if (!currentToken && 'none' === qp.prompt?.toLowerCase()) { | ||
| this._redirect(qp.redirect_uri, { | ||
| state: qp.state, | ||
| error: 'login_required', | ||
| }); | ||
| } else if (!currentToken || 'login' === qp.prompt?.toLowerCase()) { | ||
| if ('login' === qp.prompt?.toLowerCase()) { | ||
| this.auth.deleteCurrentToken(); | ||
| qp.prompt = null; | ||
| } | ||
| let { cluster_name } = this.paramsFor('vault.cluster'); | ||
| let url = this.router.urlFor(transition.to.name, transition.to.params, { queryParams: qp }); | ||
| return this.transitionTo(AUTH, cluster_name, { queryParams: { redirect_to: url } }); | ||
| } | ||
| } | ||
|
|
||
| _redirectToAuth(oidcName, queryParams, logout = false) { | ||
| let { cluster_name } = this.paramsFor('vault.cluster'); | ||
| let currentRoute = this.router.urlFor(PROVIDER, oidcName, { queryParams }); | ||
| if (logout) { | ||
| this.auth.deleteCurrentToken(); | ||
| } | ||
| return this.transitionTo(AUTH, cluster_name, { queryParams: { redirect_to: currentRoute } }); | ||
| } | ||
|
|
||
| _buildUrl(urlString, params) { | ||
| try { | ||
| let url = new URL(urlString); | ||
| Object.keys(params).forEach(key => { | ||
| if (params[key]) { | ||
| url.searchParams.append(key, params[key]); | ||
| } | ||
| }); | ||
| return url; | ||
| } catch (e) { | ||
| console.debug('DEBUG: parsing url failed for', urlString); | ||
| throw new Error('Invalid URL'); | ||
| } | ||
| } | ||
|
|
||
| _handleSuccess(response, baseUrl, state) { | ||
| const { code } = response; | ||
| let redirectUrl = this._buildUrl(baseUrl, { code, state }); | ||
| this.win.location.replace(redirectUrl); | ||
| } | ||
| _handleError(errorResp, baseUrl) { | ||
| let redirectUrl = this._buildUrl(baseUrl, { ...errorResp }); | ||
| this.win.location.replace(redirectUrl); | ||
| } | ||
|
|
||
| async model(params) { | ||
| let { oidc_name, ...qp } = params; | ||
| let decodedRedirect = decodeURI(qp.redirect_uri); | ||
| let url = this._buildUrl(`${this.win.origin}/v1/identity/oidc/provider/${oidc_name}/authorize`, qp); | ||
| try { | ||
| const response = await this.auth.ajax(url, 'GET', {}); | ||
| if ('consent' === qp.prompt?.toLowerCase()) { | ||
| return { | ||
| consent: { | ||
| code: response.code, | ||
| redirect: decodedRedirect, | ||
| state: qp.state, | ||
| }, | ||
| }; | ||
| } | ||
| this._handleSuccess(response, decodedRedirect, qp.state); | ||
| } catch (errorRes) { | ||
| let resp = await errorRes.json(); | ||
| let code = resp.error; | ||
| if (code === 'max_age_violation') { | ||
| this._redirectToAuth(oidc_name, qp, true); | ||
| } else if (code === 'invalid_redirect_uri') { | ||
| return { | ||
| error: { | ||
| title: 'Redirect URI mismatch', | ||
| message: | ||
| 'The provided redirect_uri is not in the list of allowed redirect URIs. Please make sure you are sending a valid redirect URI from your application.', | ||
| }, | ||
| }; | ||
| } else if (code === 'invalid_client_id') { | ||
| return { | ||
| error: { | ||
| title: 'Invalid client ID', | ||
| message: 'Your client ID is invalid. Please update your configuration and try again.', | ||
| }, | ||
| }; | ||
| } else { | ||
| this._handleError(resp, decodedRedirect); | ||
| } | ||
| } | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| {{#if this.didCancel}} | ||
| <h3 class="title is-3" data-test-consent-title> | ||
| Consent Not Given | ||
| </h3> | ||
| <div class="box"> | ||
| <p class="has-bottom-margin-l has-top-margin-l">Login attempt has been terminated.</p> | ||
| </div> | ||
| {{else}} | ||
| <h3 class="title is-3" data-test-consent-title> | ||
| Consent | ||
| </h3> | ||
| <form class="box" {{on 'submit' this.handleSubmit}} data-test-consent-form> | ||
| <p class="has-bottom-margin-s">In order to complete the login process, you must consent to Vault sharing your profile, email, address, and phone with the client.</p> | ||
| <p class="has-bottom-margin-s">Do you want to continue?</p> | ||
| <FormSaveButtons | ||
| @saveButtonText="Yes" | ||
| @isSaving={{false}} | ||
| @cancelButtonText="No" | ||
| @onCancel={{this.handleCancel}} | ||
| @includeBox={{false}} | ||
| /> | ||
| </form> | ||
| {{/if}} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| <div class="splash-page-container section is-flex-v-centered-tablet is-flex-1 is-fullwidth"> | ||
| <div class="columns is-centered is-gapless is-fullwidth"> | ||
| <div class="column is-4-desktop is-6-tablet"> | ||
| {{#if model.error}} | ||
| <div class="box is-shadowless is-flex-v-centered"> | ||
| <LogoEdition /> | ||
| </div> | ||
| <AlertBanner | ||
| @type="danger" | ||
| @title={{model.error.title}} | ||
| @message={{model.error.message}} | ||
| /> | ||
| {{else if model.consent}} | ||
| <OidcConsentBlock | ||
| @code={{model.consent.code}} | ||
| @state={{model.consent.state}} | ||
| @redirect={{model.consent.redirect}} | ||
| @onSuccess={{this._handleSuccess}} | ||
| /> | ||
| {{else}} | ||
| <VaultLogoSpinner /> | ||
| {{/if}} | ||
| </div> | ||
| </div> | ||
| </div> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this the new format that Meggie is requesting for features?