in authui-container/server/auth-server.ts [139:297]
private init() {
this.app.enable('trust proxy');
// Oauth handler widget code.
// Proxy these requests to <project>.firebaseapp.com.
// set this up before adding json body parser to the app. This causes POST requests to fail as mentioned in
// https://github.com/chimurai/http-proxy-middleware/issues/171#issuecomment-356218599
this.fetchAuthDomainProxyTarget().then(authDomainProxyTarget => {
log(`Proxy auth requests to target ${authDomainProxyTarget}`);
this.app.use('/__/auth/', createProxyMiddleware({
target: authDomainProxyTarget,
// set to true to pass SSL cert checks.
// This causes the SNI/Host Header of the proxy request to be set to the targetURL
// '<project>.firebaseapp.com'.
changeOrigin: true,
logLevel: 'debug',
onError: errorCallback
}));
// Support JSON-encoded bodies.
this.app.use(bodyParser.json());
// Support URL-encoded bodies.
this.app.use(bodyParser.urlencoded({
extended: true
}));
// Post requests needs bodyParser to be setup first.
// Otherwise, URLs in the request payload are not parsed correctly.
// Administrative API for writing a custom configuration to.
// This will save the configuration in a predetermined GCS bucket.
if (this.isAdminAllowed()) {
this.app.post('/set_admin_config', (req: express.Request, res: express.Response) => {
if (!req.headers.authorization ||
req.headers.authorization.split(' ').length <= 1) {
this.handleErrorResponse(res, ERROR_MAP.UNAUTHENTICATED);
} else if (!isNonNullObject(req.body) ||
Object.keys(req.body).length === 0) {
this.handleErrorResponse(res, ERROR_MAP.INVALID_ARGUMENT);
} else {
const accessToken = req.headers.authorization.split(' ')[1];
try {
// Validate config before saving it.
DefaultUiConfigBuilder.validateConfig(req.body);
this.setConfigForAdmin(accessToken, req.body).then(() => {
res.set('Content-Type', 'application/json');
res.send(JSON.stringify({
status: 200,
message: 'Changes successfully saved.',
}));
}).catch((err) => {
this.handleError(res, err);
});
} catch (e) {
this.handleErrorResponse(
res,
{
error: {
code: 400,
status: 'INVALID_ARGUMENT',
message: e.message || 'Invalid UI configuration.',
},
});
}
}
});
}
})
// Static assets.
// Note that in production, this is served from dist/server/auth-server.js.
this.app.use('/static', express.static(path.join(__dirname, '../public')));
// IAP sign-in flow.
this.app.get('/', (req: express.Request, res: express.Response) => {
// Serve content for signed in user.
return serveContentForSignIn(req, res);
});
// Provide easy way for developer to determine the auth domain being used.
// This is the location to which requests are being proxied. This is same as
// the output of /gcipConfig authDomain, but it validates that the proxy target
// was read correctly by the constructor/init() code.
this.app.get('/authdomain-proxytarget', (req: express.Request, res: express.Response) => {
this.fetchAuthDomainProxyTarget()
.then((authDomainProxyTarget) => {
res.set('Content-Type', 'text/html');
res.end(authDomainProxyTarget);
})
.catch((err) => {
this.handleError(res, err);
});
});
// Provide easy way for developer to determine version.
this.app.get('/versionz', (req: express.Request, res: express.Response) => {
res.set('Content-Type', 'text/html');
res.end(HOSTED_UI_VERSION);
});
// Developers can disable admin panel when deploying Cloud Run service.
if (this.isAdminAllowed()) {
// Administrative sign-in UI config customization.
this.app.get('/admin', (req: express.Request, res: express.Response) => {
res.set('Content-Type', 'text/html');
res.end(templates.admin({}));
});
// Administrative API for reading the current app configuration.
// This could be either saved in GCS (custom config), environment variable (custom config)
// or in memory (default config).
this.app.get('/get_admin_config', (req: express.Request, res: express.Response) => {
if (!req.headers.authorization ||
req.headers.authorization.split(' ').length <= 1) {
this.handleErrorResponse(res, ERROR_MAP.UNAUTHENTICATED);
} else {
// Use the hostname of the request to figure out the URL of the hosted UI.
// This should be used as authDomain in admin config, unless it was overridden to a different value.
// This enables authDomain to be in the same origin as the sign-in UI.
const accessToken = req.headers.authorization.split(' ')[1];
this.getConfigForAdmin(accessToken, req.hostname).then((config) => {
res.set('Content-Type', 'application/json');
res.send(JSON.stringify(config || {}));
}).catch((err) => {
this.handleError(res, err);
});
}
});
}
// Used to return the auth configuration (apiKey + authDomain).
this.app.get('/gcipConfig', (req: express.Request, res: express.Response) => {
this.gcipHandler.getGcipConfig()
.then((gcipConfig) => {
res.set('Content-Type', 'application/json');
res.send(JSON.stringify(gcipConfig));
})
.catch((err) => {
this.handleError(res, err);
});
});
// Returns the custom config (if available) or the default config, needed to render
// the sign-in UI for IAP.
this.app.get('/config', (req: express.Request, res: express.Response) => {
// Use the hostname of the request to figure out the URL of the hosted UI.
// This should be used as authDomain in config, unless it was overridden to a different value.
// This enables authDomain to be in the same origin as the sign-in UI.
this.getFallbackConfig(req.hostname)
.then((currentConfig) => {
if (!currentConfig) {
this.handleErrorResponse(res, ERROR_MAP.NOT_FOUND);
} else {
res.set('Content-Type', 'application/json');
res.send(JSON.stringify(currentConfig));
}
})
.catch((err) => {
this.handleError(res, err);
});
});
}