feat(auth): Add OIDC support (#3517) (#6549)

spec/actions.js: Add OIDC metadata fetching

components/auth/oauth2: Add OIDC URL to the Authorization popup
This commit is contained in:
Ilya Lipnitskiy
2020-12-09 10:11:33 -08:00
committed by GitHub
parent 20a89877b2
commit 0807687f91
5 changed files with 186 additions and 6 deletions

View File

@@ -122,11 +122,13 @@ export default class Oauth2 extends React.Component {
const { isOAS3 } = specSelectors
let oidcUrl = isOAS3() ? schema.get("openIdConnectUrl") : null
// Auth type consts
const IMPLICIT = "implicit"
const PASSWORD = "password"
const ACCESS_CODE = isOAS3() ? "authorizationCode" : "accessCode"
const APPLICATION = isOAS3() ? "clientCredentials" : "application"
const ACCESS_CODE = isOAS3() ? (oidcUrl ? "authorization_code" : "authorizationCode") : "accessCode"
const APPLICATION = isOAS3() ? (oidcUrl ? "client_credentials" : "clientCredentials") : "application"
let flow = schema.get("flow")
let scopes = schema.get("allowedScopes") || schema.get("scopes")
@@ -144,6 +146,7 @@ export default class Oauth2 extends React.Component {
{ isAuthorized && <h6>Authorized</h6> }
{ oidcUrl && <p>OpenID Connect URL: <code>{ oidcUrl }</code></p> }
{ ( 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> }
<p className="flow">Flow: <code>{ schema.get("flow") }</code></p>

View File

@@ -26,11 +26,13 @@ export default function authorize ( { auth, authActions, errActions, configs, au
break
case "clientCredentials":
case "client_credentials":
// OAS3
authActions.authorizeApplication(auth)
return
case "authorizationCode":
case "authorization_code":
// OAS3
query.push("response_type=code")
break

View File

@@ -8,10 +8,13 @@ import { isOAS3 as isOAS3Helper } from "../helpers"
const state = state => state
function onlyOAS3(selector) {
return (ori, system) => (state, ...args) => {
return (ori, system) => (...args) => {
const spec = system.getSystem().specSelectors.specJson()
if(isOAS3Helper(spec)) {
return selector(system, ...args)
// Pass the spec plugin state to Reselect to trigger on securityDefinitions update
let resolvedSchemes = system.getState().getIn(["spec", "resolvedSubtrees",
"components", "securitySchemes"])
return selector(system, resolvedSchemes, ...args)
} else {
return ori(...args)
}
@@ -57,6 +60,32 @@ export const definitionsToAuthorize = onlyOAS3(createSelector(
[defName]: definition
}))
}
if(type === "openIdConnect" && definition.get("openIdConnectData")) {
let oidcData = definition.get("openIdConnectData")
let grants = oidcData.get("grant_types_supported") || ["authorization_code", "implicit"]
grants.forEach((grant) => {
// Convert from OIDC list of scopes to the OAS-style map with empty descriptions
let translatedScopes = oidcData.get("scopes_supported") &&
oidcData.get("scopes_supported").reduce((acc, cur) => acc.set(cur, ""), new Map())
let translatedDef = fromJS({
flow: grant,
authorizationUrl: oidcData.get("authorization_endpoint"),
tokenUrl: oidcData.get("token_endpoint"),
scopes: translatedScopes,
type: "oauth2",
openIdConnectUrl: definition.get("openIdConnectUrl")
})
list = list.push(new Map({
[defName]: translatedDef.filter((v) => {
// filter out unset values, sometimes `authorizationUrl`
// and `tokenUrl` come out as `undefined` in the data
return v !== undefined
})
}))
})
}
})
return list

View File

@@ -150,6 +150,7 @@ const debResolveSubtrees = debounce(async () => {
errSelectors,
fn: {
resolveSubtree,
fetch,
AST = {}
},
specSelectors,
@@ -206,6 +207,28 @@ const debResolveSubtrees = debounce(async () => {
errActions.newThrownErrBatch(preparedErrors)
}
if (spec && specSelectors.isOAS3() && path[0] === "components" && path[1] === "securitySchemes") {
// Resolve OIDC URLs if present
await Promise.all(Object.values(spec)
.filter((scheme) => scheme.type === "openIdConnect")
.map(async (oidcScheme) => {
const req = {
url: oidcScheme.openIdConnectUrl,
requestInterceptor: requestInterceptor,
responseInterceptor: responseInterceptor
}
try {
const res = await fetch(req)
if (res instanceof Error || res.status >= 400) {
console.error(res.statusText + " " + req.url)
} else {
oidcScheme.openIdConnectData = JSON.parse(res.text)
}
} catch (e) {
console.error(e)
}
}))
}
set(resultMap, path, spec)
set(specWithCurrentSubtrees, path, spec)