Skip to content

Added support for '{realm}' placeholder in authorizationUrl and tokenUrl #3410

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

Open
wants to merge 13 commits into
base: master
Choose a base branch
from
Open
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
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ Config Name | Description
--- | ---
client_id | Default clientId. MUST be a string
client_secret | Default clientSecret. MUST be a string
realm | realm query parameter (for oauth1) added to `authorizationUrl` and `tokenUrl` . MUST be a string
realm | The OAuth realm parameter for `authorizationUrl` and `tokenUrl`. Optional. If specified it MUST be a string. If a `{realm}` placeholder is present in the URL path, it will be replaced with this value. If no such placeholder is present, the value will be added as the value for querystring parameter 'realm'.
Copy link
Author

Choose a reason for hiding this comment

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

The old docs were very concise. I added some more detail and mentioned the {realm} placeholder and realm query parameter explicitly so people (hopefully) understand what it does.

appName | application name, displayed in authorization popup. MUST be a string
scopeSeparator | scope separator for passing scopes, encoded before calling, default value is a space (encoded value `%20`). MUST be a string
additionalQueryStringParams | Additional query parameters added to `authorizationUrl` and `tokenUrl`. MUST be an object
Expand All @@ -126,7 +126,7 @@ const ui = SwaggerUIBundle({...})
ui.initOAuth({
clientId: "your-client-id",
clientSecret: "your-client-secret-if-required",
realm: "your-realms",
realm: "your-realm",
Copy link
Author

Choose a reason for hiding this comment

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

Afaict from all the info I've read on realm, it's usually like a container around users, roles, etc. So it seems strange to be targeting multiple realms. It for sure won't work with Keycloak. So this is why I think it is better if the example is singular.

appName: "your-app-name",
scopeSeparator: " ",
additionalQueryStringParams: {test: "hello"}
Expand Down
2 changes: 1 addition & 1 deletion dev-helpers/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@
ui.initOAuth({
clientId: "your-client-id",
clientSecret: "your-client-secret-if-required",
realm: "your-realms",
realm: "your-realm",
appName: "your-app-name",
scopeSeparator: " ",
additionalQueryStringParams: {}
Expand Down
5 changes: 2 additions & 3 deletions src/core/components/auth/auths.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -109,15 +109,14 @@ export default class Auths extends React.Component {
<p>API requires the following scopes. Select which ones you want to grant to Swagger UI.</p>
</div>
{
definitions.filter( schema => schema.get("type") === "oauth2")
.map( (schema, name) =>{
oauthDefinitions.map( (schema, name) =>{
Copy link
Author

Choose a reason for hiding this comment

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

oauthDefinitions is defined above as definitions.filter( schema => schema.get("type") === "oauth2") so repeating that code here was redundant. I stumbled upon this during my research so I fixed it... Should maybe have been a separate PR.

return (<div key={ name }>
<Oauth2 authorized={ authorized }
schema={ schema }
name={ name } />
</div>)
}
).toArray()
).toArray()
Copy link
Author

Choose a reason for hiding this comment

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

Some whitespace conversion by my editor probably, sorry.

}
</div> : null
}
Expand Down
10 changes: 6 additions & 4 deletions src/core/components/auth/oauth2.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from "react"
import PropTypes from "prop-types"
import oauth2Authorize from "core/oauth2-authorize"
import oauth2Authorize, { processUrl } from "core/oauth2-authorize"

const IMPLICIT = "implicit"
const ACCESS_CODE = "accessCode"
Expand All @@ -27,6 +27,7 @@ export default class Oauth2 extends React.Component {
let authConfigs = authSelectors.getConfigs() || {}
let username = auth && auth.get("username") || ""
let clientId = auth && auth.get("clientId") || authConfigs.clientId || ""
let realm = auth && auth.get("realm") || authConfigs.realm || ""
Copy link
Author

Choose a reason for hiding this comment

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

Make sure we get the realm setting as well

let clientSecret = auth && auth.get("clientSecret") || authConfigs.clientSecret || ""
let passwordType = auth && auth.get("passwordType") || "request-body"

Expand All @@ -39,7 +40,8 @@ export default class Oauth2 extends React.Component {
clientSecret: clientSecret,
username: username,
password: "",
passwordType: passwordType
passwordType: passwordType,
realm: realm
}
Copy link
Author

Choose a reason for hiding this comment

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

Add it to the state so we can access it later

}

Expand Down Expand Up @@ -108,8 +110,8 @@ export default class Oauth2 extends React.Component {

{ isAuthorized && <h6>Authorized</h6> }

{ ( flow === IMPLICIT || flow === ACCESS_CODE ) && <p>Authorization URL: <code>{ schema.get("authorizationUrl") }</code></p> }
{ ( flow === PASSWORD || flow === ACCESS_CODE || flow === APPLICATION ) && <p>Token URL:<code> { schema.get("tokenUrl") }</code></p> }
{ ( flow === IMPLICIT || flow === ACCESS_CODE ) && <p>Authorization URL: <code>{ processUrl(schema.get("authorizationUrl"), this.state) }</code></p> }
{ ( flow === PASSWORD || flow === ACCESS_CODE || flow === APPLICATION ) && <p>Token URL:<code> { processUrl(schema.get("tokenUrl"), this.state) }</code></p> }
Copy link
Author

Choose a reason for hiding this comment

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

Call processUrl() on the authorizationUrl and tokenUrl so we correctly fill in the {realm} and other parameters.

<p className="flow">Flow: <code>{ schema.get("flow") }</code></p>

{
Expand Down
41 changes: 28 additions & 13 deletions src/core/oauth2-authorize.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,19 +52,7 @@ export default function authorize ( { auth, authActions, errActions, configs, au

query.push("state=" + encodeURIComponent(state))

if (typeof authConfigs.realm !== "undefined") {
query.push("realm=" + encodeURIComponent(authConfigs.realm))
}

let { additionalQueryStringParams } = authConfigs

for (let key in additionalQueryStringParams) {
if (typeof additionalQueryStringParams[key] !== "undefined") {
query.push([key, additionalQueryStringParams[key]].map(encodeURIComponent).join("="))
}
}

let url = [schema.get("authorizationUrl"), query.join("&")].join("?")
const url = processUrl(schema.get("authorizationUrl"), authConfigs, query)
Copy link
Author

Choose a reason for hiding this comment

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

The removed code above is now part of processUrl


// pass action authorizeOauth2 and authentication data through window
// to authorize with oauth2
Expand All @@ -88,3 +76,30 @@ export default function authorize ( { auth, authActions, errActions, configs, au

win.open(url)
}

function processUrl(url, authConfigs, query=[]) {
let result = url || ""
if (authConfigs) {
if (authConfigs.realm) {
const placeholder = "{realm}"
const idx = url ? url.indexOf(placeholder) : -1
if (idx !== -1) {
result = url.substring(0, idx) + encodeURIComponent(authConfigs.realm) + url.substring(idx + placeholder.length)
} else {
query.push("realm=" + encodeURIComponent(authConfigs.realm))
}
}
const params = authConfigs.additionalQueryStringParams || {}
for (let key in params) {
if (params[key] !== undefined) {
query.push([key, params[key]].map(encodeURIComponent).join("="))
}
}
if (query.length) {
result += (result.indexOf("?") === -1 ? "?" : "&") + query.join("&")
}
}
return result
}
Copy link
Author

Choose a reason for hiding this comment

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

This is the meat of it. The existing code to fill in the configs and additional query params is now in this function, where I also added the bit that does the replacement of the placeholder.


export { processUrl }
9 changes: 3 additions & 6 deletions src/core/plugins/auth/actions.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import win from "core/window"
import { btoa, buildFormData } from "core/utils"
import { processUrl } from "core/oauth2-authorize"

export const SHOW_AUTH_POPUP = "show_popup"
export const AUTHORIZE = "authorize"
Expand Down Expand Up @@ -141,12 +142,8 @@ export const authorizeAccessCodeWithBasicAuthentication = ( { auth, redirectUrl

export const authorizeRequest = ( data ) => ( { fn, authActions, errActions, authSelectors } ) => {
let { body, query={}, headers={}, name, url, auth } = data
let { additionalQueryStringParams } = authSelectors.getConfigs() || {}
let fetchUrl = url

for (let key in additionalQueryStringParams) {
url += "&" + key + "=" + encodeURIComponent(additionalQueryStringParams[key])
}
Copy link
Author

Choose a reason for hiding this comment

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

This is now done by processUrl

let authConfigs = authSelectors.getConfigs() || {}
let fetchUrl = processUrl(url, authConfigs)

let _headers = Object.assign({
"Accept":"application/json, text/plain, */*",
Expand Down