plugins/auth-backend: added basic saml provider

This commit is contained in:
Patrik Oldsberg
2020-06-02 12:44:33 +02:00
parent 3c47bc4e23
commit 5d24d086f5
9 changed files with 198 additions and 8 deletions
+4
View File
@@ -20,6 +20,7 @@
"@types/passport": "^1.0.3",
"@types/passport-github2": "^1.2.4",
"@types/passport-google-oauth20": "^2.0.3",
"body-parser": "^1.19.0",
"compression": "^1.7.4",
"cookie-parser": "^1.4.5",
"cors": "^2.8.5",
@@ -31,11 +32,14 @@
"passport": "^0.4.1",
"passport-github2": "^0.1.12",
"passport-google-oauth20": "^2.0.0",
"passport-saml": "^1.3.3",
"winston": "^3.2.1",
"yn": "^4.0.0"
},
"devDependencies": {
"@backstage/cli": "^0.1.1-alpha.6",
"@types/body-parser": "^1.19.0",
"@types/passport-saml": "^1.1.2",
"jest-fetch-mock": "^3.0.3",
"tsc-watch": "^4.2.3"
},
@@ -14,4 +14,4 @@ fi
echo "Downloading and starting SAML-IdP"
export NPM_CONFIG_REGISTRY=https://registry.npmjs.org
exec npx saml-idp --acsUrl "http://localhost:3003/auth/saml/handler/frame" --audience "http://localhost:3003"
exec npx saml-idp --acsUrl "http://localhost:7000/auth/saml/handler/frame" --audience "http://localhost:7000" --port 7001
@@ -55,7 +55,7 @@ export const executeFrameHandlerStrategy = async (
reject(new Error('Unexpected redirect'));
};
strategy.authenticate(req);
strategy.authenticate(req, {});
});
};
@@ -32,4 +32,12 @@ export const providers = [
},
disableRefresh: true,
},
{
provider: 'saml',
options: {
path: '/auth/saml/handler/frame',
entryPoint: 'http://localhost:7001/',
issuer: 'passport-saml',
},
},
];
@@ -17,11 +17,13 @@
import Router from 'express-promise-router';
import { createGithubProvider } from './github';
import { createGoogleProvider } from './google';
import { createSamlProvider } from './saml';
import { AuthProviderFactory, AuthProviderConfig } from './types';
const factories: { [providerId: string]: AuthProviderFactory } = {
google: createGoogleProvider,
github: createGithubProvider,
saml: createSamlProvider,
};
export function createAuthProvider(providerId: string, config: any) {
@@ -39,6 +41,7 @@ export const createAuthProviderRouter = (config: AuthProviderConfig) => {
const router = Router();
router.get('/start', provider.start.bind(provider));
router.get('/handler/frame', provider.frameHandler.bind(provider));
router.post('/handler/frame', provider.frameHandler.bind(provider));
router.get('/logout', provider.logout.bind(provider));
if (provider.refresh) {
router.get('/refresh', provider.refresh.bind(provider));
@@ -0,0 +1,17 @@
/*
* Copyright 2020 Spotify AB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export { createSamlProvider } from './provider';
@@ -0,0 +1,82 @@
/*
* Copyright 2020 Spotify AB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import express from 'express';
import { Strategy as SamlStrategy } from 'passport-saml';
import {
executeFrameHandlerStrategy,
executeRedirectStrategy,
} from '../PassportStrategyHelper';
import { AuthProviderConfig, AuthProviderRouteHandlers } from '../types';
import { postMessageResponse } from '../OAuthProvider';
export class SamlAuthProvider implements AuthProviderRouteHandlers {
private readonly strategy: SamlStrategy;
constructor(providerConfig: AuthProviderConfig) {
this.strategy = new SamlStrategy(
{ ...providerConfig.options },
(profile: any, done: any) => {
// TODO: There's plenty more validation and profile handling to do here,
// this provider is currently only intended to validate the provider pattern
// for non-oauth auth flows.
// TODO: This flow doesn't issue an identity token that can be used to validate
// the identity of the user in other backends, which we need in some form.
done(undefined, {
email: profile.email,
firstName: profile.firstName,
lastName: profile.lastName,
displayName: profile.displayName,
});
},
);
}
async start(req: express.Request, res: express.Response): Promise<any> {
const { url } = await executeRedirectStrategy(req, this.strategy, {});
res.redirect(url);
}
async frameHandler(
req: express.Request,
res: express.Response,
): Promise<any> {
try {
const { user } = await executeFrameHandlerStrategy(req, this.strategy);
return postMessageResponse(res, {
type: 'auth-result',
payload: user,
});
} catch (error) {
return postMessageResponse(res, {
type: 'auth-result',
error: {
name: error.name,
message: error.message,
},
});
}
}
async logout(_req: express.Request, res: express.Response): Promise<any> {
res.send('noop');
}
}
export function createSamlProvider(config: AuthProviderConfig) {
return new SamlAuthProvider(config);
}
@@ -17,6 +17,7 @@
import express from 'express';
import Router from 'express-promise-router';
import cookieParser from 'cookie-parser';
import bodyParser from 'body-parser';
import { Logger } from 'winston';
import { providers } from './../providers/config';
import { createAuthProviderRouter } from '../providers';
@@ -32,6 +33,8 @@ export async function createRouter(
const logger = options.logger.child({ plugin: 'auth' });
router.use(cookieParser());
router.use(bodyParser.urlencoded({ extended: false }));
router.use(bodyParser.json());
// configure all the providers
for (const providerConfig of providers) {