Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Potentially handle logout differently #1

Open
wparad opened this issue Nov 23, 2023 · 0 comments
Open

Potentially handle logout differently #1

wparad opened this issue Nov 23, 2023 · 0 comments
Labels
enhancement New feature or request

Comments

@wparad
Copy link
Member

wparad commented Nov 23, 2023

  public async generateLogoutRequest(user: Profile, options: LogoutOptions) : Promise<string> {
    const id = options.generateUniqueId();
    const instant = generateInstant();

    const request = {
      "samlp:LogoutRequest": {
        "@xmlns:samlp": "urn:oasis:names:tc:SAML:2.0:protocol",
        "@xmlns:saml": "urn:oasis:names:tc:SAML:2.0:assertion",
        "@ID": id,
        "@Version": "2.0",
        "@IssueInstant": instant,
        "@Destination": options.logoutUrl,
        "saml:Issuer": {
          "@xmlns:saml": "urn:oasis:names:tc:SAML:2.0:assertion",
          "#text": options.issuer,
        },
        "saml:NameID": {
          "@Format": user!.nameIDFormat,
          "#text": user!.nameID,
        },
      },
    } as LogoutRequestXML;

    if (user!.nameQualifier != null) {
      request["samlp:LogoutRequest"]["saml:NameID"]["@NameQualifier"] = user!.nameQualifier;
    }

    if (user!.spNameQualifier != null) {
      request["samlp:LogoutRequest"]["saml:NameID"]["@SPNameQualifier"] = user!.spNameQualifier;
    }

    if (user!.sessionIndex) {
      request["samlp:LogoutRequest"]["saml2p:SessionIndex"] = {
        "@xmlns:saml2p": "urn:oasis:names:tc:SAML:2.0:protocol",
        "#text": user!.sessionIndex,
      };
    }

    await this.cacheProvider.save(id, instant);
    const request = buildXmlBuilderObject(request, false);
    await this._requestToUrl(request, null, "logout");
  }

  public async generateLogoutResponse(user: Profile, options: LogoutOptions) : Promise<string> {
    const id = options.generateUniqueId();
    const instant = generateInstant();

    const request = {
      "samlp:LogoutResponse": {
        "@xmlns:samlp": "urn:oasis:names:tc:SAML:2.0:protocol",
        "@xmlns:saml": "urn:oasis:names:tc:SAML:2.0:assertion",
        "@ID": id,
        "@Version": "2.0",
        "@IssueInstant": instant,
        "@Destination": options.logoutUrl,
        "@InResponseTo": logoutRequest.ID,
        "saml:Issuer": {
          "#text": options.issuer,
        },
        "samlp:Status": {
          "samlp:StatusCode": {
            "@Value": "urn:oasis:names:tc:SAML:2.0:status:Success",
          },
        },
      },
    };

    return buildXmlBuilderObject(request, false);
  }

  private async _requestToUrl(
    request: string | null | undefined,
    response: string | null,
    operation: string,
    additionalParameters: querystring.ParsedUrlQuery
  ): Promise<string> {
    providerSingleSignOnUrl = assertRequired(options.providerSingleSignOnUrl, "providerSingleSignOnUrl is required");

    let buffer: Buffer;
    if (options.skipRequestCompression) {
      buffer = Buffer.from((request || response)!, "utf8");
    } else {
      buffer = await deflateRaw((request || response)!);
    }

    const base64 = buffer.toString("base64");
    let target = new URL(providerSingleSignOnUrl);

    if (operation === "logout") {
      if (options.logoutUrl) {
        target = new URL(options.logoutUrl);
      }
    } else if (operation !== "authorize") {
      throw new Error("Unknown operation: " + operation);
    }

    const samlMessage: querystring.ParsedUrlQuery = request
      ? {
          SAMLRequest: base64,
        }
      : {
          SAMLResponse: base64,
        };
    Object.keys(additionalParameters).forEach((k) => {
      samlMessage[k] = additionalParameters[k];
    });
    if (options.privateKey != null) {
      if (!providerSingleSignOnUrl) {
        throw new Error('"providerSingleSignOnUrl" config parameter is required for signed messages');
      }

      // sets .SigAlg and .Signature
      this.signRequest(samlMessage);
    }
    Object.keys(samlMessage).forEach((k) => {
      target.searchParams.set(k, samlMessage[k] as string);
    });

    return target.toString();
  }

  public async getLogoutResponseUrl(options: LogoutResponseOptions) : Promise<string> {
    const response = this._generateLogoutResponse(samlLogoutRequest);
    return await this._requestToUrl(null, response, 'logout');
  }
@wparad wparad added the enhancement New feature or request label Nov 23, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

1 participant