From 4e78c523d1d7be08dfe6c47ef84b473eaacc98f8 Mon Sep 17 00:00:00 2001 From: Mathieu Dutour Date: Fri, 18 Dec 2020 20:53:30 +0100 Subject: [PATCH] [SAML2-js] update SAML2-js types for version 2.x (#49787) * update saml2-js types * add mathieudutour to definition author * fix lint * fix jsdoc alignment --- types/saml2-js/index.d.ts | 123 +++++++++++++++++++++++++++---- types/saml2-js/saml2-js-tests.ts | 102 ++++++++++++------------- 2 files changed, 158 insertions(+), 67 deletions(-) diff --git a/types/saml2-js/index.d.ts b/types/saml2-js/index.d.ts index 56e2aff9b5..2dfb266ace 100644 --- a/types/saml2-js/index.d.ts +++ b/types/saml2-js/index.d.ts @@ -1,75 +1,172 @@ -// Type definitions for SAML2-js 1.6.0 +// Type definitions for SAML2-js 2.0.6 // Project: https://github.com/Clever/saml2 // Definitions by: horiuchi +// mathieudutour // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped // TypeScript Version: 2.3 -declare module "saml2-js" { - +declare module 'saml2-js' { + /** Represents an online service that authenticates users in the SAML flow. */ export class IdentityProvider { constructor(options: IdentityProviderOptions); } + export interface IdentityProviderOptions { + /** Login url to use during a login request. */ sso_login_url: string; + /** Logout url to use during a logout request. */ sso_logout_url: string; - certificates: string[]; + /** Certificate or certificates (array of certificate) for the identity provider. */ + certificates: string | string[]; + /** If true, forces re-authentication of users even if the user has a SSO session with the IdP. This can also be configured on the SP or on a per-method basis. */ force_authn?: boolean; + /** If true, signs the request. This can also be configured on the SP or on a per-method basis. */ sign_get_request?: boolean; + /** If true, allows unencrypted assertions. This can also be configured on the SP or on a per-method basis. */ allow_unencrypted_assertion?: boolean; } - + /** Represents a service provider that relies on a trusted IdentityProvider for authentication and authorization in the SAML flow. */ export class ServiceProvider { constructor(options: ServiceProviderOptions); - create_login_request_url(IdP: IdentityProvider, options: CreateLoginRequestUrlOptions, cb: (error: any, login_url: string, request_id: string) => void): void; - redirect_assert(IdP: IdentityProvider, options: GetAssertOptions, cb: (error: any, response: any) => void): void; - post_assert(IdP: IdentityProvider, options: GetAssertOptions, cb: (error: any, response: any) => void): void; - create_logout_request_url(IdP: IdentityProvider, options: CreateLogoutRequestUrlOptions, cb: (error: any, request_url: string) => void): void; - create_logout_response_url(IdP: IdentityProvider, options: CreateLogoutResponseUrlOptions, cb: (error: any, response_url: string) => void): void; + /** Get a URL to initiate a login. */ + create_login_request_url( + IdP: IdentityProvider, + options: CreateLoginRequestUrlOptions, + cb: (error: Error | null, login_url: string, request_id: string) => void, + ): void; + /** Gets a SAML response object if the login attempt is valid, used for redirect binding. */ + redirect_assert( + IdP: IdentityProvider, + options: RedirectAssertOptions, + cb: (error: Error | null, response: SAMLAssertResponse) => void, + ): void; + /** Gets a SAML response object if the login attempt is valid, used for post binding. */ + post_assert( + IdP: IdentityProvider, + options: PostAssertOptions, + cb: (error: Error | null, response: SAMLAssertResponse) => void, + ): void; + /** Creates a SAML Request URL to initiate a user logout. */ + create_logout_request_url( + IdP: IdentityProvider | string, + options: CreateLogoutRequestUrlOptions, + cb: (error: Error | null, request_url: string, request_id: string) => void, + ): void; + /** Creates a SAML Response URL to confirm a successful IdP initiated logout. */ + create_logout_response_url( + IdP: IdentityProvider | string, + options: CreateLogoutResponseUrlOptions, + cb: (error: Error | null, response_url: string) => void, + ): void; + /** Returns the XML metadata used during the initial SAML configuration. */ create_metadata(): string; } + export interface ServiceProviderOptions { + /** Unique identifier for the service provider, often the URL of the metadata file. */ entity_id: string; + /** Private key for the service provider. */ private_key: string; + /** Certificate for the service provider. */ certificate: string; + /** URL of service provider assert endpoint. */ assert_endpoint: string; + /** Additional private keys to use when attempting to decrypt responses. Useful for adding backward-compatibility for old certificates after a rollover. */ alt_private_keys?: string[]; + /** Additional certificates to expose in the SAML metadata. Useful for staging new certificates for rollovers. */ alt_certs?: string[]; + /** If set, at least one of the values within the condition of a SAML authentication response must match. Defaults to `entity_id`. */ + audience?: string | RegExp; + /** + * To account for clock skew between IdP and SP, accept responses with a NotBefore condition + * ahead of the current time (according to our clock) by this number of seconds. + * + * Defaults to 1. + * Set it to 0 for optimum security but no tolerance for clock skew. + */ + notbefore_skew?: number; + /** If true, forces re-authentication of users even if the user has a SSO session with the IdP. This can also be configured on the IdP or on a per-method basis. */ force_authn?: boolean; + /** Specifies AuthnContextClassRef. This can also be configured on a per-method basis. */ auth_context?: AuthnContextClassRef; + /** Format for Name ID. This can also be configured on a per-method basis. */ nameid_format?: string; + /** If true, signs the request. This can also be configured on the IdP or on a per-method basis. */ sign_get_request?: boolean; + /** If true, allows unencrypted assertions. This can also be configured on the IdP or on a per-method basis. */ allow_unencrypted_assertion?: boolean; } export interface CreateLoginRequestUrlOptions { + /** SAML relay state. */ relay_state?: string; + /** Specifies AuthnContextClassRef. This can also be configured on the SP. */ auth_context?: AuthnContextClassRef; + /** Format for Name ID. This can also be configured on the SP. */ nameid_format?: string; + /** If true, forces re-authentication of users even if the user has a SSO session with the IdP. This can also be configured on the IdP or SP. */ force_authn?: boolean; + /** If true, signs the request. This can also be configured on the IdP or SP. */ sign_get_request?: boolean; } - export interface GetAssertOptions { - request_body?: any; + export interface RedirectAssertOptions { + /** An object containing the parsed query string parameters. This object should contain the value for either a SAMLResponse or SAMLRequest. */ + request_body: { + SAMLResponse?: any; + SAMLRequest?: any; + }; + /** If true, allows unencrypted assertions. This can also be configured on the IdP or SP. */ allow_unencrypted_assertion?: boolean; + /** If false, allow the assertion to be valid without a SessionIndex attribute on the AuthnStatement node. */ + require_session_index?: boolean; + } + export interface PostAssertOptions extends RedirectAssertOptions { + /** If set, at least one of the values within the condition of a SAML authentication response must match. Defaults to entity_id. */ + audience?: string | RegExp; + /** + * To account for clock skew between IdP and SP, accept responses with a NotBefore condition + * ahead of the current time (according to our clock) by this number of seconds. + * + * Defaults to 1. + * Set it to 0 for optimum security but no tolerance for clock skew. + */ + notbefore_skew?: boolean; } export interface CreateLogoutRequestUrlOptions { + /** Format for Name ID. This can also be configured on a per-method basis. */ name_id?: string; + /** Session index to use for creating logout request. */ session_index?: string; + /** If true, allows unencrypted assertions. This can also be configured on the IdP or SP. */ allow_unencrypted_assertion?: boolean; + /** If true, signs the request. This can also be configured on the IdP or SP. */ sign_get_request?: boolean; + /** SAML relay state. */ relay_state?: string; } export interface CreateLogoutResponseUrlOptions { + /** The ID of the request that this is in response to. Should be checked against any sent request IDs. */ in_response_to?: string; + /** If true, signs the request. This can also be configured on the IdP or SP. */ sign_get_request?: boolean; + /** SAML relay state. */ relay_state?: string; } + export interface SAMLAssertResponse { + response_header: { id: 'string'; destination: string; in_response_to: string }; + type: string; + user: { + name_id: string; + session_index?: string; + session_not_on_or_after?: string; + attributes?: { [attr: string]: string | string[] }; + }; + } export interface AuthnContextClassRef { comparison: string; class_refs: string[]; } - } diff --git a/types/saml2-js/saml2-js-tests.ts b/types/saml2-js/saml2-js-tests.ts index 4fd1867b67..8bbf8d2233 100644 --- a/types/saml2-js/saml2-js-tests.ts +++ b/types/saml2-js/saml2-js-tests.ts @@ -2,19 +2,18 @@ import * as fs from 'fs'; import express = require('express'); import * as saml2 from 'saml2-js'; - // Example { const sp_options = { - entity_id: "https://sp.example.com/metadata.xml", - private_key: fs.readFileSync("key-file.pem").toString(), - certificate: fs.readFileSync("cert-file.crt").toString(), - assert_endpoint: "https://sp.example.com/assert", + entity_id: 'https://sp.example.com/metadata.xml', + private_key: fs.readFileSync('key-file.pem').toString(), + certificate: fs.readFileSync('cert-file.crt').toString(), + assert_endpoint: 'https://sp.example.com/assert', force_authn: true, - auth_context: { comparison: "exact", class_refs: ["urn:oasis:names:tc:SAML:1.0:am:password"] }, - nameid_format: "urn:oasis:names:tc:SAML:2.0:nameid-format:transient", + auth_context: { comparison: 'exact', class_refs: ['urn:oasis:names:tc:SAML:1.0:am:password'] }, + nameid_format: 'urn:oasis:names:tc:SAML:2.0:nameid-format:transient', sign_get_request: false, - allow_unencrypted_assertion: true + allow_unencrypted_assertion: true, }; // Call service provider constructor with options @@ -26,12 +25,12 @@ import * as saml2 from 'saml2-js'; // Initialize options object const idp_options = { - sso_login_url: "https://idp.example.com/login", - sso_logout_url: "https://idp.example.com/logout", - certificates: [fs.readFileSync("cert-file1.crt").toString(), fs.readFileSync("cert-file2.crt").toString()], + sso_login_url: 'https://idp.example.com/login', + sso_logout_url: 'https://idp.example.com/logout', + certificates: [fs.readFileSync('cert-file1.crt').toString(), fs.readFileSync('cert-file2.crt').toString()], force_authn: true, sign_get_request: false, - allow_unencrypted_assertion: false + allow_unencrypted_assertion: false, }; // Call identity provider constructor with options @@ -39,80 +38,75 @@ import * as saml2 from 'saml2-js'; // Example usage of identity provider. // Pass identity provider into a service provider function with options and a callback. - sp.post_assert(idp, {}, (error: any, response: any) => {}); + sp.post_assert(idp, { request_body: { SAMLRequest: {} } }, (error: any, response: any) => {}); } - // Example: Express implementation { const app = express(); // Create service provider const sp_options = { - entity_id: "https://sp.example.com/metadata.xml", - private_key: fs.readFileSync("key-file.pem").toString(), - certificate: fs.readFileSync("cert-file.crt").toString(), - assert_endpoint: "https://sp.example.com/assert" + entity_id: 'https://sp.example.com/metadata.xml', + private_key: fs.readFileSync('key-file.pem').toString(), + certificate: fs.readFileSync('cert-file.crt').toString(), + assert_endpoint: 'https://sp.example.com/assert', }; const sp = new saml2.ServiceProvider(sp_options); // Create identity provider const idp_options = { - sso_login_url: "https://idp.example.com/login", - sso_logout_url: "https://idp.example.com/logout", - certificates: [fs.readFileSync("cert-file1.crt").toString(), fs.readFileSync("cert-file2.crt").toString()] + sso_login_url: 'https://idp.example.com/login', + sso_logout_url: 'https://idp.example.com/logout', + certificates: [fs.readFileSync('cert-file1.crt').toString(), fs.readFileSync('cert-file2.crt').toString()], }; const idp = new saml2.IdentityProvider(idp_options); // ------ Define express endpoints ------ // Endpoint to retrieve metadata - app.get("/metadata.xml", function(req, res) { - res.type('application/xml'); - res.send(sp.create_metadata()); + app.get('/metadata.xml', function(req, res) { + res.type('application/xml'); + res.send(sp.create_metadata()); }); // Starting point for login - app.get("/login", function(req, res) { - sp.create_login_request_url(idp, {}, function(err, login_url, request_id) { - if (err != null) - return res.send(500); - res.redirect(login_url); - }); + app.get('/login', function(req, res) { + sp.create_login_request_url(idp, {}, function(err, login_url, request_id) { + if (err != null) return res.send(500); + res.redirect(login_url); + }); }); // Assert endpoint for when login completes - app.post("/assert", function(req, res) { - const options = {request_body: req.body}; - sp.post_assert(idp, options, function(err, saml_response) { - if (err != null) - return res.send(500); + app.post('/assert', function(req, res) { + const options = { request_body: req.body }; + sp.post_assert(idp, options, function(err, saml_response) { + if (err != null) return res.send(500); - // Save name_id and session_index for logout - // Note: In practice these should be saved in the user session, not globally. - let name_id = saml_response.user.name_id; - let session_index = saml_response.user.session_index; + // Save name_id and session_index for logout + // Note: In practice these should be saved in the user session, not globally. + let name_id = saml_response.user.name_id; + let session_index = saml_response.user.session_index; - res.send("Hello #{saml_response.user.name_id}!"); - }); + res.send('Hello #{saml_response.user.name_id}!'); + }); }); // Starting point for logout - app.get("/logout", function(req, res) { - let name_id = ''; - let session_index = ''; - const options = { - name_id: name_id, - session_index: session_index - }; + app.get('/logout', function(req, res) { + let name_id = ''; + let session_index = ''; + const options = { + name_id: name_id, + session_index: session_index, + }; - sp.create_logout_request_url(idp, options, function(err, logout_url) { - if (err != null) - return res.send(500); - res.redirect(logout_url); - }); + sp.create_logout_request_url(idp, options, function(err, logout_url) { + if (err != null) return res.send(500); + res.redirect(logout_url); + }); }); app.listen(3000); } -