Skip to content

Commit

Permalink
feat(auth): Add OIDC support (#3517) (#6549)
Browse files Browse the repository at this point in the history
spec/actions.js: Add OIDC metadata fetching

components/auth/oauth2: Add OIDC URL to the Authorization popup
  • Loading branch information
lipnitsk committed Dec 9, 2020
1 parent 20a8987 commit 0807687
Show file tree
Hide file tree
Showing 5 changed files with 186 additions and 6 deletions.
7 changes: 5 additions & 2 deletions src/core/components/auth/oauth2.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand All @@ -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>
Expand Down
2 changes: 2 additions & 0 deletions src/core/oauth2-authorize.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
33 changes: 31 additions & 2 deletions src/core/plugins/oas3/auth-extensions/wrap-selectors.js
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down Expand Up @@ -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
Expand Down
23 changes: 23 additions & 0 deletions src/core/plugins/spec/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ const debResolveSubtrees = debounce(async () => {
errSelectors,
fn: {
resolveSubtree,
fetch,
AST = {}
},
specSelectors,
Expand Down Expand Up @@ -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)

Expand Down
127 changes: 125 additions & 2 deletions test/unit/core/plugins/oas3/wrap-auth-selectors.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@

import { fromJS } from "immutable"
import { fromJS, Map } from "immutable"
import {
definitionsToAuthorize
} from "corePlugins/oas3/auth-extensions/wrap-selectors"
Expand All @@ -12,6 +12,7 @@ describe("oas3 plugin - auth extensions - wrapSelectors", function(){
// Given
const system = {
getSystem: () => system,
getState: () => new Map(),
specSelectors: {
specJson: () => fromJS({
openapi: "3.0.0"
Expand Down Expand Up @@ -53,7 +54,39 @@ describe("oas3 plugin - auth extensions - wrapSelectors", function(){
}
}
}
}
},
"oidc": {
"type": "openIdConnect",
"openIdConnectUrl": "https://accounts.google.com/.well-known/openid-configuration",
"openIdConnectData": {
"authorization_endpoint": "https://accounts.google.com/o/oauth2/v2/auth",
"token_endpoint": "https://oauth2.googleapis.com/token",
"scopes_supported": [
"openid",
"email",
"profile"
],
"grant_types_supported": [
"authorization_code",
"refresh_token",
"urn:ietf:params:oauth:grant-type:device_code",
"urn:ietf:params:oauth:grant-type:jwt-bearer"
]
}
},
"oidcNoGrant": {
"type": "openIdConnect",
"openIdConnectUrl": "https://accounts.google.com/.well-known/openid-configuration",
"openIdConnectData": {
"authorization_endpoint": "https://accounts.google.com/o/oauth2/v2/auth",
"token_endpoint": "https://oauth2.googleapis.com/token",
"scopes_supported": [
"openid",
"email",
"profile"
]
},
},
})
}
}
Expand Down Expand Up @@ -106,6 +139,96 @@ describe("oas3 plugin - auth extensions - wrapSelectors", function(){
type: "oauth2"
}
},
{
oidc: {
flow: "authorization_code",
authorizationUrl: "https://accounts.google.com/o/oauth2/v2/auth",
tokenUrl: "https://oauth2.googleapis.com/token",
openIdConnectUrl: "https://accounts.google.com/.well-known/openid-configuration",
scopes: {
"openid": "",
"email": "",
"profile": "",
},
type: "oauth2"
}
},
{
oidc: {
flow: "refresh_token",
authorizationUrl: "https://accounts.google.com/o/oauth2/v2/auth",
tokenUrl: "https://oauth2.googleapis.com/token",
openIdConnectUrl: "https://accounts.google.com/.well-known/openid-configuration",
scopes: {
"openid": "",
"email": "",
"profile": "",
},
type: "oauth2"
}
},
{
oidc: {
flow: "urn:ietf:params:oauth:grant-type:device_code",
authorizationUrl: "https://accounts.google.com/o/oauth2/v2/auth",
tokenUrl: "https://oauth2.googleapis.com/token",
openIdConnectUrl: "https://accounts.google.com/.well-known/openid-configuration",
scopes: {
"openid": "",
"email": "",
"profile": "",
},
type: "oauth2"
}
},
{
oidc: {
flow: "urn:ietf:params:oauth:grant-type:jwt-bearer",
authorizationUrl: "https://accounts.google.com/o/oauth2/v2/auth",
tokenUrl: "https://oauth2.googleapis.com/token",
openIdConnectUrl: "https://accounts.google.com/.well-known/openid-configuration",
scopes: {
"openid": "",
"email": "",
"profile": "",
},
type: "oauth2"
}
},
{
// See https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata
// grant_types_supported
// OPTIONAL. JSON array containing a list of the OAuth 2.0 Grant Type values that
// this OP supports. Dynamic OpenID Providers MUST support the authorization_code
// and implicit Grant Type values and MAY support other Grant Types. If omitted,
// the default value is ["authorization_code", "implicit"].
oidcNoGrant: {
flow: "authorization_code",
authorizationUrl: "https://accounts.google.com/o/oauth2/v2/auth",
tokenUrl: "https://oauth2.googleapis.com/token",
openIdConnectUrl: "https://accounts.google.com/.well-known/openid-configuration",
scopes: {
"openid": "",
"email": "",
"profile": "",
},
type: "oauth2"
}
},
{
oidcNoGrant: {
flow: "implicit",
authorizationUrl: "https://accounts.google.com/o/oauth2/v2/auth",
tokenUrl: "https://oauth2.googleapis.com/token",
openIdConnectUrl: "https://accounts.google.com/.well-known/openid-configuration",
scopes: {
"openid": "",
"email": "",
"profile": "",
},
type: "oauth2"
}
},
])

})
Expand Down

0 comments on commit 0807687

Please sign in to comment.