in server/auth/types/saml/routes.ts [38:241]
public setupRoutes() {
this.router.get(
{
path: `/auth/saml/login`,
validate: {
query: schema.object({
nextUrl: schema.maybe(
schema.string({
validate: validateNextUrl,
})
),
}),
},
options: {
authRequired: false,
},
},
async (context, request, response) => {
if (request.auth.isAuthenticated) {
return response.redirected({
headers: {
location: `${this.coreSetup.http.basePath.serverBasePath}/app/opensearch-dashboards`,
},
});
}
try {
const samlHeader = await this.securityClient.getSamlHeader(request);
const cookie: SecuritySessionCookie = {
saml: {
nextUrl: request.query.nextUrl,
requestId: samlHeader.requestId,
},
};
this.sessionStorageFactory.asScoped(request).set(cookie);
return response.redirected({
headers: {
location: samlHeader.location,
},
});
} catch (error) {
context.security_plugin.logger.error(`Failed to get saml header: ${error}`);
return response.internalError(); // TODO: redirect to error page?
}
}
);
this.router.post(
{
path: `/_opendistro/_security/saml/acs`,
validate: {
body: schema.any(),
},
options: {
authRequired: false,
},
},
async (context, request, response) => {
let requestId: string = '';
let nextUrl: string = '/';
try {
const cookie = await this.sessionStorageFactory.asScoped(request).get();
if (cookie) {
requestId = cookie.saml?.requestId || '';
nextUrl =
cookie.saml?.nextUrl ||
`${this.coreSetup.http.basePath.serverBasePath}/app/opensearch-dashboards`;
}
if (!requestId) {
return response.badRequest({
body: 'Invalid requestId',
});
}
} catch (error) {
context.security_plugin.logger.error(`Failed to parse cookie: ${error}`);
return response.badRequest();
}
try {
const credentials = await this.securityClient.authToken(
requestId,
request.body.SAMLResponse,
undefined
);
const user = await this.securityClient.authenticateWithHeader(
request,
'authorization',
credentials.authorization
);
let expiryTime = Date.now() + this.config.session.ttl;
const [headerEncoded, payloadEncoded, signature] = credentials.authorization.split('.');
if (!payloadEncoded) {
context.security_plugin.logger.error('JWT token payload not found');
}
const tokenPayload = JSON.parse(Buffer.from(payloadEncoded, 'base64').toString());
if (tokenPayload.exp) {
expiryTime = parseInt(tokenPayload.exp, 10) * 1000;
}
const cookie: SecuritySessionCookie = {
username: user.username,
credentials: {
authHeaderValue: credentials.authorization,
},
authType: 'saml', // TODO: create constant
expiryTime,
};
this.sessionStorageFactory.asScoped(request).set(cookie);
return response.redirected({
headers: {
location: nextUrl,
},
});
} catch (error) {
context.security_plugin.logger.error(
`SAML SP initiated authentication workflow failed: ${error}`
);
}
return response.internalError();
}
);
this.router.post(
{
path: `/_opendistro/_security/saml/acs/idpinitiated`,
validate: {
body: schema.any(),
},
options: {
authRequired: false,
},
},
async (context, request, response) => {
const acsEndpoint = `${this.coreSetup.http.basePath.serverBasePath}/_opendistro/_security/saml/acs/idpinitiated`;
try {
const credentials = await this.securityClient.authToken(
undefined,
request.body.SAMLResponse,
acsEndpoint
);
const user = await this.securityClient.authenticateWithHeader(
request,
'authorization',
credentials.authorization
);
let expiryTime = Date.now() + this.config.session.ttl;
const [headerEncoded, payloadEncoded, signature] = credentials.authorization.split('.');
if (!payloadEncoded) {
context.security_plugin.logger.error('JWT token payload not found');
}
const tokenPayload = JSON.parse(Buffer.from(payloadEncoded, 'base64').toString());
if (tokenPayload.exp) {
expiryTime = parseInt(tokenPayload.exp, 10) * 1000;
}
const cookie: SecuritySessionCookie = {
username: user.username,
credentials: {
authHeaderValue: credentials.authorization,
},
authType: 'saml', // TODO: create constant
expiryTime,
};
this.sessionStorageFactory.asScoped(request).set(cookie);
return response.redirected({
headers: {
location: `${this.coreSetup.http.basePath.serverBasePath}/app/opensearch-dashboards`,
},
});
} catch (error) {
context.security_plugin.logger.error(
`SAML IDP initiated authentication workflow failed: ${error}`
);
}
return response.internalError();
}
);
this.router.get(
{
path: `/auth/logout`,
validate: false,
},
async (context, request, response) => {
try {
const authInfo = await this.securityClient.authinfo(request);
this.sessionStorageFactory.asScoped(request).clear();
// TODO: need a default logout page
const redirectUrl =
authInfo.sso_logout_url || this.coreSetup.http.basePath.serverBasePath || '/';
return response.redirected({
headers: {
location: redirectUrl,
},
});
} catch (error) {
context.security_plugin.logger.error(`SAML logout failed: ${error}`);
return response.badRequest();
}
}
);
}