Use configuration url to retrieve oidc endpoints
The /.well-known/openid-configuration endpoint can be used to retrieve the majority of configuration needed for oidc Additionally refactor the callback url to be generated on the server side as this is a fixed endpoint. Add linting fixes
This commit is contained in:
parent
baab7141c0
commit
7803540399
|
@ -2,7 +2,14 @@ const passport = require("koa-passport")
|
||||||
const LocalStrategy = require("passport-local").Strategy
|
const LocalStrategy = require("passport-local").Strategy
|
||||||
const JwtStrategy = require("passport-jwt").Strategy
|
const JwtStrategy = require("passport-jwt").Strategy
|
||||||
const { StaticDatabases } = require("./db/utils")
|
const { StaticDatabases } = require("./db/utils")
|
||||||
const { jwt, local, authenticated, google, oidc, auditLog } = require("./middleware")
|
const {
|
||||||
|
jwt,
|
||||||
|
local,
|
||||||
|
authenticated,
|
||||||
|
google,
|
||||||
|
oidc,
|
||||||
|
auditLog,
|
||||||
|
} = require("./middleware")
|
||||||
const { setDB, getDB } = require("./db")
|
const { setDB, getDB } = require("./db")
|
||||||
|
|
||||||
// Strategies
|
// Strategies
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
const env = require("../../environment")
|
const env = require("../../environment")
|
||||||
const jwt = require("jsonwebtoken")
|
const jwt = require("jsonwebtoken")
|
||||||
const database = require("../../db")
|
const database = require("../../db")
|
||||||
|
const fetch = require("node-fetch")
|
||||||
const OIDCStrategy = require("@techpass/passport-openidconnect").Strategy
|
const OIDCStrategy = require("@techpass/passport-openidconnect").Strategy
|
||||||
const {
|
const {
|
||||||
StaticDatabases,
|
StaticDatabases,
|
||||||
|
@ -9,7 +10,17 @@ const {
|
||||||
} = require("../../db/utils")
|
} = require("../../db/utils")
|
||||||
|
|
||||||
// async function authenticate(token, tokenSecret, profile, done) {
|
// async function authenticate(token, tokenSecret, profile, done) {
|
||||||
async function authenticate(issuer, sub, profile, jwtClaims, accessToken, refreshToken, idToken, params, done) {
|
async function authenticate(
|
||||||
|
issuer,
|
||||||
|
sub,
|
||||||
|
profile,
|
||||||
|
jwtClaims,
|
||||||
|
accessToken,
|
||||||
|
refreshToken,
|
||||||
|
idToken,
|
||||||
|
params,
|
||||||
|
done
|
||||||
|
) {
|
||||||
// Check the user exists in the instance DB by email
|
// Check the user exists in the instance DB by email
|
||||||
const db = database.getDB(StaticDatabases.GLOBAL.name)
|
const db = database.getDB(StaticDatabases.GLOBAL.name)
|
||||||
|
|
||||||
|
@ -18,7 +29,7 @@ async function authenticate(issuer, sub, profile, jwtClaims, accessToken, refres
|
||||||
const userId = generateGlobalUserID(profile.id)
|
const userId = generateGlobalUserID(profile.id)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// use the google profile id
|
// use the OIDC profile id
|
||||||
dbUser = await db.get(userId)
|
dbUser = await db.get(userId)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
const user = {
|
const user = {
|
||||||
|
@ -28,13 +39,13 @@ async function authenticate(issuer, sub, profile, jwtClaims, accessToken, refres
|
||||||
...profile._json,
|
...profile._json,
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if an account with the google email address exists locally
|
// check if an account with the OIDC email address exists locally
|
||||||
const users = await db.query(`database/${ViewNames.USER_BY_EMAIL}`, {
|
const users = await db.query(`database/${ViewNames.USER_BY_EMAIL}`, {
|
||||||
key: profile._json.email,
|
key: profile._json.email,
|
||||||
include_docs: true,
|
include_docs: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
// Google user already exists by email
|
// OIDC user already exists by email
|
||||||
if (users.rows.length > 0) {
|
if (users.rows.length > 0) {
|
||||||
const existing = users.rows[0].doc
|
const existing = users.rows[0].doc
|
||||||
|
|
||||||
|
@ -74,36 +85,41 @@ async function authenticate(issuer, sub, profile, jwtClaims, accessToken, refres
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create an instance of the google passport strategy. This wrapper fetches the configuration
|
* Create an instance of the oidc passport strategy. This wrapper fetches the configuration
|
||||||
* from couchDB rather than environment variables, using this factory is necessary for dynamically configuring passport.
|
* from couchDB rather than environment variables, using this factory is necessary for dynamically configuring passport.
|
||||||
* @returns Dynamically configured Passport Google Strategy
|
* @returns Dynamically configured Passport OIDC Strategy
|
||||||
*/
|
*/
|
||||||
exports.strategyFactory = async function () {
|
exports.strategyFactory = async function (callbackUrl) {
|
||||||
try {
|
try {
|
||||||
|
const configurationUrl =
|
||||||
|
"https://login.microsoftonline.com/2668c0dd-7ed2-4db3-b387-05b6f9204a70/v2.0/.well-known/openid-configuration"
|
||||||
|
const clientSecret = "g-ty~2iW.bo.88xj_QI6~hdc-H8mP2Xbnd"
|
||||||
|
const clientId = "bed2017b-2f53-42a9-8ef9-e58918935e07"
|
||||||
|
|
||||||
/*
|
if (!clientId || !clientSecret || !callbackUrl || !configurationUrl) {
|
||||||
const { clientID, clientSecret, callbackURL } = config
|
|
||||||
|
|
||||||
if (!clientID || !clientSecret || !callbackURL) {
|
|
||||||
throw new Error(
|
throw new Error(
|
||||||
"Configuration invalid. Must contain google clientID, clientSecret and callbackURL"
|
"Configuration invalid. Must contain clientID, clientSecret, callbackUrl and configurationUrl"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
return new OIDCStrategy(
|
const response = await fetch(configurationUrl)
|
||||||
{
|
if (response.ok) {
|
||||||
issuer: "https://base.uri/auth/realms/realm_name",
|
const body = await response.json()
|
||||||
authorizationURL: "https://base.uri/auth/realms/realm_name/protocol/openid-connect/auth",
|
|
||||||
tokenURL: "https://base.uri/auth/realms/realm_name/protocol/openid-connect/token",
|
return new OIDCStrategy(
|
||||||
userInfoURL: "https://base.uri/auth/realms/realm_name/protocol/openid-connect/userinfo",
|
{
|
||||||
clientID: "my_client_id",
|
issuer: body.issuer,
|
||||||
clientSecret: "my_client_secret",
|
authorizationURL: body.authorization_endpoint,
|
||||||
callbackURL: "http://localhost:10000/api/admin/auth/oidc/callback",
|
tokenURL: body.token_endpoint,
|
||||||
scope: "openid profile email",
|
userInfoURL: body.userinfo_endpoint,
|
||||||
},
|
clientID: clientId,
|
||||||
authenticate
|
clientSecret: clientSecret,
|
||||||
)
|
callbackURL: callbackUrl,
|
||||||
|
scope: "profile email",
|
||||||
|
},
|
||||||
|
authenticate
|
||||||
|
)
|
||||||
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err)
|
console.error(err)
|
||||||
throw new Error("Error constructing OIDC authentication strategy", err)
|
throw new Error("Error constructing OIDC authentication strategy", err)
|
||||||
|
|
|
@ -1,15 +1,12 @@
|
||||||
<script>
|
<script>
|
||||||
import { ActionButton } from "@budibase/bbui"
|
import { ActionButton } from "@budibase/bbui"
|
||||||
import { admin } from "stores/portal"
|
// import { admin } from "stores/portal"
|
||||||
|
|
||||||
let show = true
|
let show = true
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if show}
|
{#if show}
|
||||||
<ActionButton
|
<ActionButton on:click={() => window.open("/api/admin/auth/oidc", "_blank")}>
|
||||||
on:click={() => window.open("/api/admin/auth/oidc", "_blank")}
|
|
||||||
>
|
|
||||||
<div class="inner">
|
<div class="inner">
|
||||||
<p>Sign in with OIDC</p>
|
<p>Sign in with OIDC</p>
|
||||||
</div>
|
</div>
|
||||||
|
@ -25,10 +22,10 @@
|
||||||
padding-top: var(--spacing-xs);
|
padding-top: var(--spacing-xs);
|
||||||
padding-bottom: var(--spacing-xs);
|
padding-bottom: var(--spacing-xs);
|
||||||
}
|
}
|
||||||
.inner img {
|
/* .inner img {
|
||||||
width: 18px;
|
width: 18px;
|
||||||
margin: 3px 10px 3px 3px;
|
margin: 3px 10px 3px 3px;
|
||||||
}
|
} */
|
||||||
.inner p {
|
.inner p {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -131,10 +131,17 @@ exports.googleAuth = async (ctx, next) => {
|
||||||
)(ctx, next)
|
)(ctx, next)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Minimal OIDC attempt
|
async function oidcStrategyFactory(ctx) {
|
||||||
|
const callbackUrl = `${ctx.protocol}://${ctx.host}/api/admin/auth/oidc/callback`
|
||||||
|
return oidc.strategyFactory(callbackUrl)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The initial call that OIDC authentication makes to take you to the configured OIDC login screen.
|
||||||
|
* On a successful login, you will be redirected to the oidcAuth callback route.
|
||||||
|
*/
|
||||||
exports.oidcPreAuth = async (ctx, next) => {
|
exports.oidcPreAuth = async (ctx, next) => {
|
||||||
const strategy = await oidc.strategyFactory()
|
const strategy = await oidcStrategyFactory(ctx)
|
||||||
|
|
||||||
return passport.authenticate(strategy, {
|
return passport.authenticate(strategy, {
|
||||||
scope: ["profile", "email"],
|
scope: ["profile", "email"],
|
||||||
|
@ -142,7 +149,7 @@ exports.oidcPreAuth = async (ctx, next) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.oidcAuth = async (ctx, next) => {
|
exports.oidcAuth = async (ctx, next) => {
|
||||||
const strategy = await oidc.strategyFactory()
|
const strategy = await oidcStrategyFactory(ctx)
|
||||||
|
|
||||||
return passport.authenticate(
|
return passport.authenticate(
|
||||||
strategy,
|
strategy,
|
||||||
|
|
|
@ -14,7 +14,7 @@ const redis = require("./utilities/redis")
|
||||||
|
|
||||||
const app = new Koa()
|
const app = new Koa()
|
||||||
|
|
||||||
app.keys = ['secret', 'key'];
|
app.keys = ["secret", "key"]
|
||||||
|
|
||||||
// set up top level koa middleware
|
// set up top level koa middleware
|
||||||
app.use(koaBody({ multipart: true }))
|
app.use(koaBody({ multipart: true }))
|
||||||
|
|
Loading…
Reference in New Issue