Skip to content

Commit

Permalink
portal authorization
Browse files Browse the repository at this point in the history
  • Loading branch information
vzakharchenko committed Jun 2, 2020
1 parent 289bbc8 commit d6e83a2
Show file tree
Hide file tree
Showing 24 changed files with 7,215 additions and 3 deletions.
21 changes: 21 additions & 0 deletions example/keycloak-cloudfront-portal/README.md
@@ -0,0 +1,21 @@
# Cloudfront(Lambda:edge) with portal authorization

## 1. Start Keycloak

### Docker
Using the image from https://hub.docker.com/r/jboss/keycloak/
```
docker run -p 8090:8080 -e JAVA_OPTS="-Dkeycloak.profile.feature.scripts=enabled -Dkeycloak.profile.feature.upload_scripts=enabled -server -Xms64m -Xmx512m -XX:MetaspaceSize=96M -XX:MaxMetaspaceSize=256m -Djava.net.preferIPv4Stack=true -Djboss.modules.system.pkgs=org.jboss.byteman -Djava.awt.headless=true" -e KEYCLOAK_USER=admin -e KEYCLOAK_PASSWORD=admin jboss/keycloak
```
change JWKS URL for SecurityRealm1:
replace with http://<YOUR DEVICE IP>:8080/cert instead of http://localhost:8080/cert
change JWKS URL for SecurityRealm2:
replace with http://<YOUR DEVICE IP>:8080/cert instead of http://localhost:8080/cert
### Standard
```
sh bin/standalone.sh -c standalone.xml -b 0.0.0.0 -Djboss.bind.address.management=0.0.0.0 --debug 8190 -Djboss.http.port=8090
```
### Import Realms
**Open the Keycloak admin console, click on Add Realm, click on import 'Select file', select portal.realm, realm1.json and realm2.json and click Create.
**

43 changes: 43 additions & 0 deletions example/keycloak-cloudfront-portal/babel.config.js
@@ -0,0 +1,43 @@
module.exports = function (api) {
api.cache(true);

const presets = [
[
'@babel/preset-env',
{
targets: {
esmodules: true,
},
},
],
['@babel/preset-react'],
['@babel/preset-flow']
];

const plugins = [
['@babel/transform-runtime', {
helpers: false,
regenerator: true,
}],
['@babel/plugin-proposal-decorators', { legacy: true }],
['@babel/plugin-proposal-class-properties', { loose: true }],
'@babel/plugin-transform-object-assign',
'@babel/plugin-proposal-do-expressions',
'@babel/plugin-proposal-export-default-from',
'@babel/plugin-proposal-object-rest-spread',
'@babel/plugin-proposal-function-sent',
'@babel/plugin-proposal-optional-chaining',
'@babel/plugin-proposal-partial-application',
];

if (process.env.NODE_ENV === 'test') {
plugins.push('babel-plugin-dynamic-import-node');
} else {
plugins.push('@babel/plugin-syntax-dynamic-import');
}

return {
presets,
plugins,
};
};
10 changes: 10 additions & 0 deletions example/keycloak-cloudfront-portal/index.html
@@ -0,0 +1,10 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>CloudFront/Lambda:edge example</title>
</head>
<body>
<div id="root"></div>
</body>
</html>
@@ -0,0 +1,33 @@
module.exports = function (api) {
api.cache(true);

const presets = [
[
'@babel/preset-env',
{
targets: {
esmodules: true,
},
},
],
];
const plugins = [
['@babel/transform-runtime', {
helpers: false,
regenerator: true,
}],
['@babel/plugin-proposal-decorators', { decoratorsBeforeExport: true }],
['@babel/plugin-proposal-object-rest-spread'],
];

if (process.env.NODE_ENV === 'test') {
plugins.push('babel-plugin-dynamic-import-node');
} else {
plugins.push('@babel/plugin-syntax-dynamic-import');
}

return {
presets,
plugins,
};
};
@@ -0,0 +1,4 @@
import { authorization } from './src/index';

// eslint-disable-next-line import/prefer-default-export
export const lambda = authorization;
@@ -0,0 +1,7 @@
module.exports = {
setupFiles: ['./unitTestConfig/jestSetup.js'],
transform: {
'^.+\\.js$': 'babel-jest',
},
transformIgnorePatterns: ['<rootDir>/node_modules/'],
};
@@ -0,0 +1,45 @@
{
"name": "lambda-edge-example",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"build": "NODE_ENV=production webpack --config webpack.config.babel.js",
"link:dep": "cd ../../.. && npm link && cd example/keycloak-cloudfront/lambda-edge-example && npm link keycloak-lambda-authorizer",
"test": "jest --maxWorkers=2"
},
"author": "vzakharchenko",
"license": "Apache-2.0",
"devDependencies": {
"@babel/core": "^7.10.2",
"@babel/plugin-proposal-decorators": "^7.10.1",
"@babel/plugin-proposal-object-rest-spread": "^7.10.1",
"@babel/plugin-transform-runtime": "^7.10.1",
"@babel/preset-env": "^7.10.2",
"@babel/register": "^7.10.1",
"babel-jest": "^26.0.1",
"babel-loader": "^8.1.0",
"enzyme": "^3.11.0",
"jest": "^26.0.1",
"jest-date-mock": "^1.0.8",
"progress-bar-webpack-plugin": "^2.1.0",
"uglifyjs-webpack-plugin": "^2.2.0",
"webpack": "^4.43.0",
"webpack-cli": "^3.3.11"
},
"dependencies": {
"@babel/runtime": "^7.10.2",
"axios": "^0.19.2",
"cookie": "^0.4.1",
"crypto-js": "^4.0.0",
"jsonwebtoken": "^8.5.1",
"jws": "^4.0.0",
"keycloak-lambda-authorizer": "../../../",
"node-cache": "^5.1.0",
"node-forge": "^0.9.1",
"querystring": "^0.2.0",
"rsa-pem-to-jwk": "^1.1.3",
"keycloak-cloudfront-dynamodb": "^0.1.4",
"uuid": "^8.1.0"
}
}
@@ -0,0 +1,68 @@
import { privateKey, publicKey } from './sessionKeys';

export const portalKeycloakJSON = {
realm: 'portal',
'auth-server-url': 'http://127.0.0.1:8090/auth/',
'ssl-required': 'external',
resource: 'portal-ui',
credentials: {
secret: 'ece2012c-fc01-4dca-b50b-f898c9c6c811',
},
'confidential-port': 0,
};

export const tenant1KeycloakJson = {
realm: 'securityRealm2',
'auth-server-url': 'http://localhost:8090/auth/',
'ssl-required': 'external',
resource: 'tenant1Client',
'verify-token-audience': true,
credentials: {
jwt: { },
},
'confidential-port': 0,
'policy-enforcer': {},
};

export const tenant1Options = {
enforce: {
enabled: true,
resource: {
name: 'tenant1Resource',
},
},
};


export const tenant2KeycloakJson = {
realm: 'securityRealm2',
'auth-server-url': 'http://localhost:8090/auth',
'ssl-required': 'external',
resource: 'tenant2Client',
'verify-token-audience': false,
credentials: {
jwt: {
'client-key-password': 'REPLACE WITH THE KEY PASSWORD IN KEYSTORE',
'client-keystore-file': 'REPLACE WITH THE LOCATION OF YOUR KEYSTORE FILE',
'client-keystore-password': 'REPLACE WITH THE KEYSTORE PASSWORD',
'client-key-alias': 'tenant2Client',
'token-timeout': 10,
'client-keystore-type': 'jks',
},
},
'confidential-port': 0,
'policy-enforcer': {},
};

export const tenant2Options = {
keys: {
privateKey,
publicKey,
},
enforce: {
enabled: true,
resource: {
name: 'tenant2Resource',
},
},
};
@@ -0,0 +1,76 @@
import { lamdaEdge } from 'keycloak-lambda-authorizer';
import { SessionManager } from 'keycloak-lambda-authorizer/src/edge/storage/SessionManager';
import { LocalSessionStorage } from 'keycloak-lambda-authorizer/src/edge/storage/localSessionStorage';
import { DynamoDbSessionStorage } from 'keycloak-cloudfront-dynamodb/DynamoDbSessionStorage';
import { privateKey, publicKey } from './sessionKeys';
import {
tenant1KeycloakJson,
tenant1Options,
tenant2KeycloakJson,
tenant2Options,
portalKeycloakJSON,
} from './Tentants';

const keycloakJson1 = tenant1KeycloakJson;
const keycloakJson2 = tenant2KeycloakJson;

function tenant2ResponseHandler(request, options) {
const { uri } = request;
const keycloakJson = options.keycloakJson(options);
if (uri.startsWith(`/${keycloakJson.realm}/api`)) {
return {
status: '200',
statusDescription: 'OK',
body: JSON.stringify({ tenant2: 'success' }),
};
}
return request;
}
lamdaEdge.routes.addJwksEndpoint('/cert', publicKey.key);
lamdaEdge.routes.addProtected(
['tenant2.html', keycloakJson2.realm],
keycloakJson2,
{
...tenant2Options,
...{ responseHandler: tenant2ResponseHandler },
},
);

function tenant1ResponseHandler(request, options) {
const { uri } = request;
const keycloakJson = options.keycloakJson(options);
if (uri.startsWith(`/${keycloakJson.realm}/api`)) {
return {
status: '200',
statusDescription: 'OK',
body: JSON.stringify({ tenant1: 'success' }),
};
}
return request;
}

lamdaEdge.routes.addProtected(
['tenant1.html', keycloakJson1.realm],
keycloakJson1,
{
...tenant1Options,
...{ responseHandler: tenant1ResponseHandler },
},
);
lamdaEdge.routes.addProtected(
['/', keycloakJson1.realm],
portalKeycloakJSON,
);
// eslint-disable-next-line import/prefer-default-export
export async function authorization(event, context, callback) {
await lamdaEdge.lambdaEdgeRouter(event, context, new SessionManager(
event.localhost ? new LocalSessionStorage()
: new DynamoDbSessionStorage({ region: 'us-east-1' }, 'exampleSessionTable'),
{
keys: {
privateKey,
publicKey,
},
},
), callback);
}
@@ -0,0 +1,60 @@

const publicKey = '-----BEGIN CERTIFICATE-----\n'
+ 'MIIDgzCCAmugAwIBAgIJAJTi4Mu+7fIMMA0GCSqGSIb3DQEBCwUAMFgxCzAJBgNV\n'
+ 'BAYTAlVTMQ8wDQYDVQQIDAZEZW5pYWwxFDASBgNVBAcMC1NwcmluZ2ZpZWxkMQww\n'
+ 'CgYDVQQKDANEaXMxFDASBgNVBAMMC2xhbWJkYS1qd2tzMB4XDTIwMDQxMjA3NDUy\n'
+ 'MFoXDTIxMDQxMjA3NDUyMFowWDELMAkGA1UEBhMCVVMxDzANBgNVBAgMBkRlbmlh\n'
+ 'bDEUMBIGA1UEBwwLU3ByaW5nZmllbGQxDDAKBgNVBAoMA0RpczEUMBIGA1UEAwwL\n'
+ 'bGFtYmRhLWp3a3MwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCqtsnm\n'
+ 'VbhKd9X1rkNalJO27OmMgF9jT8olOHuyqft1xKak9B04huWWGbCj6Q03Ugp3z9ZP\n'
+ 'vcowITEuaQPCRrkLCndTO8K6wSOJ1ygW8tWPkkN0okyiMVb35+0mdqHV/f1F+9PB\n'
+ 'IUgFh5F9L3HXtP42pk3NbNfP//o7BAjyN2NBiN+scz2dE/wORJE1sgLi3ICXTp6K\n'
+ 'XvzNlXwpWfGv4bRuTxJZ8XAjJfElihtQ6pUKDjRyYMFnx2OltFjSfTl5BiA02slF\n'
+ 'TDrf7UjGc3srihCnwCl377MqhCTYK9/NZEkBkAH1yasoWjv1NSXDNAyLGLjlE1w2\n'
+ 'SHQ/MwFDAZkig25HAgMBAAGjUDBOMB0GA1UdDgQWBBRD0CkykEVkbA74qJghVg9F\n'
+ 'DflQgzAfBgNVHSMEGDAWgBRD0CkykEVkbA74qJghVg9FDflQgzAMBgNVHRMEBTAD\n'
+ 'AQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAm69bZRa104V7ftm5Sl4BYhH5BGYq+QJG2\n'
+ '+vS8S56GO07ExrD+pEPW0LUeFICVf33XFrN3QFGk87MJQVDac7GxV43JB4qsOCj0\n'
+ 'Sbe0Vyu+oY/wVthtdv3Z7GuwLS/OsIWRKtYxA0IvIDlQyXXmh8E9fL+ZTNKo0iSi\n'
+ 'p/H5lWGBSNrMKZNkjPJ59n6EKiNChMrNsz+nk5C8efhEqopToMfJKr1swI0/jP7l\n'
+ '9kmEAZePVJamyYJt+hyN89E5wFvEYuzTOXkQpHZB7achhLnepAZTeWD1814WNHaU\n'
+ '/qN33+fW7+7N3qHDm1YGWrxUBnFdczEguG6l2MSHTBVMk+/rp60B\n'
+ '-----END CERTIFICATE-----\n';

const privateKey = '-----BEGIN PRIVATE KEY-----\n'
+ 'MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCqtsnmVbhKd9X1\n'
+ 'rkNalJO27OmMgF9jT8olOHuyqft1xKak9B04huWWGbCj6Q03Ugp3z9ZPvcowITEu\n'
+ 'aQPCRrkLCndTO8K6wSOJ1ygW8tWPkkN0okyiMVb35+0mdqHV/f1F+9PBIUgFh5F9\n'
+ 'L3HXtP42pk3NbNfP//o7BAjyN2NBiN+scz2dE/wORJE1sgLi3ICXTp6KXvzNlXwp\n'
+ 'WfGv4bRuTxJZ8XAjJfElihtQ6pUKDjRyYMFnx2OltFjSfTl5BiA02slFTDrf7UjG\n'
+ 'c3srihCnwCl377MqhCTYK9/NZEkBkAH1yasoWjv1NSXDNAyLGLjlE1w2SHQ/MwFD\n'
+ 'AZkig25HAgMBAAECggEAdU/fNrW5SxNGqOnzxw9K4u2zIKYm5qwyEZnbB0/gSXG1\n'
+ 'wq0uV2X750YIKNtCBb4PC357m5iklKZ6kZYAy0SmbHvou/3ZN1T6AwMjvYFqWJr+\n'
+ 'V+wgFWUqinmKcmAbnl5H6gu/3Hvubj5XMFumM8Fg4FUwKfad54XUgzGmpCyDvMge\n'
+ '+jJYTQDmZaDGFKyc+LXeT/W1r53Xzq4IesZsv91CA4P7sxuSH3+2SIIeKTLBhhxd\n'
+ 'wzjdZ7jZhOWnxsGcJaQWS0T9mbeRnIPT153bNBYPIaP4p5yPgUdzad/UyGiY6nO1\n'
+ 'xJ48Pf6/WspK7t54zRTEJkb9gZvu8w2pMyupAMBW4QKBgQDSMOeKx1cStL9K4APV\n'
+ '8j7PH//sbKBQea3pV1aC9EiYhj9kmeaIEGHKZBc6nTaJ8tCvET67jymPqJLBqyPU\n'
+ 'ef87YelAgD3XV42vorTHXJX+9DU58YKpblirdjVYpcoPNlke3cRde01NGw5S6JSh\n'
+ 'e9nNIjTccVqZTjLNdSrrgmKWuwKBgQDP61xMuaCypuaL/mYoLiZfgpuVjO7nAooy\n'
+ 'KMM/Z0D34tfRRWbCLOdchz0iWT+0F7rTzY1uOuW3cYdKdU7IS2aBPO0p6rPgCsOY\n'
+ 'WNO36a9zxPEghshm21lDZYl8t4Wp5PjnXdjepequ0KNYrfVj2rar8V83cDOWarAN\n'
+ 'PJwyXSi75QKBgGP0uce3cGMG7Ylv6qMNpmzdbNlD9yEOHHRBAnUYMoXGIdN3lLfU\n'
+ 'Ao06+Aj5xnvnqvH2I30SYdNdeRz8g/eBZK0arM/trHsBufFyUMIV94bdH4rEnTxx\n'
+ 'q10uw8O6Y9LEJ7GUCNPj1Sj72t32mOgKe9Mflz/V8B3DoEkwlQ6WXMgNAoGAGflB\n'
+ '94e87nRxGo32PxC81HOhcgZAFfW4Q9nZwkLo186rvUXZN2qaoHF4jqDtl1bbjPgB\n'
+ 'sgKDje4Nw5xx8g2RSZXN3s2mGNffZVm7YR89Ps4cfT65LDg8p3G4wi6+8OFcwrJz\n'
+ 'lCTP83S24y4gGJBK/6HQjkFjAGhlg9HNhXEj1I0CgYBm2UOnqZ+c6Eg4m0kNlcbW\n'
+ 'PLJjiOd1ahc7lSMkep6kXG9MKqiyvfvbbIkRLIxU7s8W+TG0vNJxUrXzWg9FM3Sp\n'
+ 'JEr8I4E1mzB26LwvEame9bGtV9rJJEKH1JgcL5L4Yny52vAGUoC8n4bN6vRb51M2\n'
+ 'aSV+AcDJyQBwjvRjN8kfdQ==\n'
+ '-----END PRIVATE KEY-----\n';

module.exports = {
privateKey: {
key: privateKey,
},
publicKey: {
key: publicKey,
},
};

0 comments on commit d6e83a2

Please sign in to comment.