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 JwtStrategy = require("passport-jwt").Strategy
|
||||
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")
|
||||
|
||||
// Strategies
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
const env = require("../../environment")
|
||||
const jwt = require("jsonwebtoken")
|
||||
const database = require("../../db")
|
||||
const fetch = require("node-fetch")
|
||||
const OIDCStrategy = require("@techpass/passport-openidconnect").Strategy
|
||||
const {
|
||||
StaticDatabases,
|
||||
|
@ -9,7 +10,17 @@ const {
|
|||
} = require("../../db/utils")
|
||||
|
||||
// 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
|
||||
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)
|
||||
|
||||
try {
|
||||
// use the google profile id
|
||||
// use the OIDC profile id
|
||||
dbUser = await db.get(userId)
|
||||
} catch (err) {
|
||||
const user = {
|
||||
|
@ -28,13 +39,13 @@ async function authenticate(issuer, sub, profile, jwtClaims, accessToken, refres
|
|||
...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}`, {
|
||||
key: profile._json.email,
|
||||
include_docs: true,
|
||||
})
|
||||
|
||||
// Google user already exists by email
|
||||
// OIDC user already exists by email
|
||||
if (users.rows.length > 0) {
|
||||
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.
|
||||
* @returns Dynamically configured Passport Google Strategy
|
||||
* @returns Dynamically configured Passport OIDC Strategy
|
||||
*/
|
||||
exports.strategyFactory = async function () {
|
||||
exports.strategyFactory = async function (callbackUrl) {
|
||||
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"
|
||||
|
||||
/*
|
||||
const { clientID, clientSecret, callbackURL } = config
|
||||
|
||||
if (!clientID || !clientSecret || !callbackURL) {
|
||||
if (!clientId || !clientSecret || !callbackUrl || !configurationUrl) {
|
||||
throw new Error(
|
||||
"Configuration invalid. Must contain google clientID, clientSecret and callbackURL"
|
||||
"Configuration invalid. Must contain clientID, clientSecret, callbackUrl and configurationUrl"
|
||||
)
|
||||
}
|
||||
*/
|
||||
|
||||
const response = await fetch(configurationUrl)
|
||||
if (response.ok) {
|
||||
const body = await response.json()
|
||||
|
||||
return new OIDCStrategy(
|
||||
{
|
||||
issuer: "https://base.uri/auth/realms/realm_name",
|
||||
authorizationURL: "https://base.uri/auth/realms/realm_name/protocol/openid-connect/auth",
|
||||
tokenURL: "https://base.uri/auth/realms/realm_name/protocol/openid-connect/token",
|
||||
userInfoURL: "https://base.uri/auth/realms/realm_name/protocol/openid-connect/userinfo",
|
||||
clientID: "my_client_id",
|
||||
clientSecret: "my_client_secret",
|
||||
callbackURL: "http://localhost:10000/api/admin/auth/oidc/callback",
|
||||
scope: "openid profile email",
|
||||
issuer: body.issuer,
|
||||
authorizationURL: body.authorization_endpoint,
|
||||
tokenURL: body.token_endpoint,
|
||||
userInfoURL: body.userinfo_endpoint,
|
||||
clientID: clientId,
|
||||
clientSecret: clientSecret,
|
||||
callbackURL: callbackUrl,
|
||||
scope: "profile email",
|
||||
},
|
||||
authenticate
|
||||
)
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
throw new Error("Error constructing OIDC authentication strategy", err)
|
||||
|
|
|
@ -1,15 +1,12 @@
|
|||
<script>
|
||||
import { ActionButton } from "@budibase/bbui"
|
||||
import { admin } from "stores/portal"
|
||||
// import { admin } from "stores/portal"
|
||||
|
||||
let show = true
|
||||
|
||||
</script>
|
||||
|
||||
{#if show}
|
||||
<ActionButton
|
||||
on:click={() => window.open("/api/admin/auth/oidc", "_blank")}
|
||||
>
|
||||
<ActionButton on:click={() => window.open("/api/admin/auth/oidc", "_blank")}>
|
||||
<div class="inner">
|
||||
<p>Sign in with OIDC</p>
|
||||
</div>
|
||||
|
@ -25,10 +22,10 @@
|
|||
padding-top: var(--spacing-xs);
|
||||
padding-bottom: var(--spacing-xs);
|
||||
}
|
||||
.inner img {
|
||||
/* .inner img {
|
||||
width: 18px;
|
||||
margin: 3px 10px 3px 3px;
|
||||
}
|
||||
} */
|
||||
.inner p {
|
||||
margin: 0;
|
||||
}
|
||||
|
|
|
@ -131,10 +131,17 @@ exports.googleAuth = async (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) => {
|
||||
const strategy = await oidc.strategyFactory()
|
||||
const strategy = await oidcStrategyFactory(ctx)
|
||||
|
||||
return passport.authenticate(strategy, {
|
||||
scope: ["profile", "email"],
|
||||
|
@ -142,7 +149,7 @@ exports.oidcPreAuth = async (ctx, next) => {
|
|||
}
|
||||
|
||||
exports.oidcAuth = async (ctx, next) => {
|
||||
const strategy = await oidc.strategyFactory()
|
||||
const strategy = await oidcStrategyFactory(ctx)
|
||||
|
||||
return passport.authenticate(
|
||||
strategy,
|
||||
|
|
|
@ -14,7 +14,7 @@ const redis = require("./utilities/redis")
|
|||
|
||||
const app = new Koa()
|
||||
|
||||
app.keys = ['secret', 'key'];
|
||||
app.keys = ["secret", "key"]
|
||||
|
||||
// set up top level koa middleware
|
||||
app.use(koaBody({ multipart: true }))
|
||||
|
|
Loading…
Reference in New Issue