Full Code of russellhaering/gosaml2 for AI

main 636e7dda202a cached
166 files
1.2 MB
706.9k tokens
264 symbols
1 requests
Download .txt
Showing preview only (1,252K chars total). Download the full file or copy to clipboard to get everything.
Repository: russellhaering/gosaml2
Branch: main
Commit: 636e7dda202a
Files: 166
Total size: 1.2 MB

Directory structure:
gitextract_zio5e6rg/

├── .github/
│   ├── dependabot.yml
│   └── workflows/
│       ├── scorecard.yml
│       └── test.yml
├── .gitignore
├── LICENSE
├── README.md
├── SECURITY.md
├── attribute.go
├── authn_request.go
├── build_logout_response.go
├── build_request.go
├── build_request_test.go
├── decode_logout_request.go
├── decode_response.go
├── decode_response_test.go
├── go.mod
├── go.sum
├── internal/
│   └── fuzz/
│       ├── README.md
│       └── fuzz_test.go
├── logout_request.go
├── oss-fuzz/
│   ├── Dockerfile
│   ├── README.md
│   ├── build.sh
│   ├── fuzz_decode_response.options
│   └── project.yaml
├── providertests/
│   ├── exercise.go
│   ├── exercise_go_1_6.go
│   ├── oktadev_test.go
│   ├── onelogin_test.go
│   ├── pingfed_test.go
│   ├── providers_test.go
│   ├── testdata/
│   │   ├── adfs_idp_signing_cert.pem
│   │   ├── adfs_response.b64
│   │   ├── adfs_response_enc.b64
│   │   ├── auth0_cert.pem
│   │   ├── auth0_response.xml
│   │   ├── oam_cert.pem
│   │   ├── oam_response.xml
│   │   ├── okta_cert.pem
│   │   ├── okta_response.xml
│   │   ├── oktaenc_idp_signing_cert.pem
│   │   ├── oktaenc_response.b64
│   │   ├── oktaenc_sp_encryption_cert.pem
│   │   ├── oktaenc_sp_encryption_key.pem
│   │   ├── oktaenc_sp_signing_cert.pem
│   │   ├── oktaenc_sp_signing_key.pem
│   │   ├── onelogin/
│   │   │   ├── idp.signing.cert
│   │   │   ├── olgn09_response_01.b64
│   │   │   ├── olgn09_response_03.b64
│   │   │   ├── olgn09_response_04.b64
│   │   │   ├── olgn09_response_05.b64
│   │   │   ├── olgn09_response_06.b64
│   │   │   ├── olgn09_response_07.b64
│   │   │   ├── olgn09_response_08.b64
│   │   │   ├── olgn09_response_09.b64
│   │   │   ├── olgn09_response_11.b64
│   │   │   ├── olgn09_response_12.b64
│   │   │   ├── olgn09_response_13.b64
│   │   │   ├── olgn09_response_14.b64
│   │   │   ├── olgn09_response_15.b64
│   │   │   ├── olgn09_response_155.b64
│   │   │   ├── olgn09_response_156.b64
│   │   │   ├── olgn09_response_157.b64
│   │   │   ├── olgn09_response_158.b64
│   │   │   ├── olgn09_response_159.b64
│   │   │   ├── olgn09_response_16.b64
│   │   │   ├── olgn09_response_17.b64
│   │   │   ├── olgn09_response_18.b64
│   │   │   ├── olgn09_response_21.b64
│   │   │   ├── olgn09_response_22.b64
│   │   │   ├── olgn09_response_23.b64
│   │   │   ├── olgn09_response_24.b64
│   │   │   ├── olgn09_response_25.b64
│   │   │   ├── olgn09_response_26.b64
│   │   │   ├── olgn09_response_27.b64
│   │   │   ├── olgn09_response_28.b64
│   │   │   ├── olgn09_response_31.b64
│   │   │   ├── olgn09_response_33.b64
│   │   │   ├── olgn09_response_34.b64
│   │   │   ├── olgn09_response_36.b64
│   │   │   ├── olgn09_response_37.b64
│   │   │   ├── olgn09_response_38.b64
│   │   │   ├── olgn09_response_46.b64
│   │   │   ├── olgn09_response_47.b64
│   │   │   ├── olgn09_response_48.b64
│   │   │   ├── olgn09_response_50.b64
│   │   │   ├── olgn09_response_51.b64
│   │   │   ├── olgn09_response_52.b64
│   │   │   ├── olgn09_response_53.b64
│   │   │   ├── olgn09_response_54.b64
│   │   │   ├── olgn09_response_55.b64
│   │   │   ├── olgn09_response_56.b64
│   │   │   ├── olgn09_response_57.b64
│   │   │   ├── olgn09_response_58.b64
│   │   │   ├── olgn09_response_59.b64
│   │   │   ├── olgn09_response_81.b64
│   │   │   ├── olgn09_response_82.b64
│   │   │   ├── olgn09_response_83.b64
│   │   │   ├── olgn09_response_84.b64
│   │   │   ├── olgn09_response_85.b64
│   │   │   ├── olgn09_response_86.b64
│   │   │   ├── olgn09_response_87.b64
│   │   │   ├── olgn09_response_88.b64
│   │   │   ├── olgn09_response_89.b64
│   │   │   ├── olgn09_response_91.b64
│   │   │   ├── olgn09_response_92.b64
│   │   │   ├── olgn09_response_93.b64
│   │   │   ├── olgn09_response_94.b64
│   │   │   ├── olgn09_response_97.b64
│   │   │   ├── olgn09_response_98.b64
│   │   │   ├── olgn09_response_99.b64
│   │   │   ├── sp.encryption.cert
│   │   │   ├── sp.encryption.key
│   │   │   ├── sp.signing.cert
│   │   │   └── sp.signing.key
│   │   ├── onelogin_cert.pem
│   │   ├── onelogin_response.xml
│   │   ├── pingfed/
│   │   │   ├── idp.signing.cert
│   │   │   ├── pfed11_response_01.b64
│   │   │   ├── pfed11_response_02.b64
│   │   │   ├── pfed11_response_03.b64
│   │   │   ├── pfed11_response_05.b64
│   │   │   ├── sp.encryption.cert
│   │   │   ├── sp.encryption.key
│   │   │   ├── sp.signing.cert
│   │   │   └── sp.signing.key
│   │   └── saml.oktadev.com/
│   │       ├── oktadev.pem
│   │       ├── response_0
│   │       ├── response_1
│   │       ├── response_10
│   │       ├── response_11
│   │       ├── response_12
│   │       ├── response_13
│   │       ├── response_14
│   │       ├── response_15
│   │       ├── response_16
│   │       ├── response_2
│   │       ├── response_3
│   │       ├── response_4
│   │       ├── response_5
│   │       ├── response_6
│   │       ├── response_7
│   │       ├── response_8
│   │       └── response_9
│   └── utils.go
├── retrieve_assertion.go
├── run_test.sh
├── s2example/
│   └── demo.go
├── saml.go
├── saml_test.go
├── test_constants.go
├── testdata/
│   ├── assertion.xml
│   ├── idp.crt
│   ├── saml.post
│   ├── saml.xml
│   ├── saml_compressed.post
│   ├── test.crt
│   └── test.key
├── types/
│   ├── encrypted_assertion.go
│   ├── encrypted_key.go
│   ├── metadata.go
│   └── response.go
├── uuid/
│   ├── uuid.go
│   └── uuid_test.go
├── validate.go
└── xml_constants.go

================================================
FILE CONTENTS
================================================

================================================
FILE: .github/dependabot.yml
================================================
version: 2
updates:
  - package-ecosystem: "github-actions"
    directory: "/"
    schedule:
      interval: "weekly"

  - package-ecosystem: "gomod"
    directory: "/"
    schedule:
      interval: "weekly"


================================================
FILE: .github/workflows/scorecard.yml
================================================
name: Scorecard supply-chain security
on:
  # For Branch-Protection check. Only the default branch is supported. See
  # https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection
  branch_protection_rule:
  # To guarantee Maintained check is occasionally updated. See
  # https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained
  schedule:
    - cron: '33 5 * * 2'
  push:
    branches: [ "main" ]

# Declare default permissions as read only.
permissions: read-all

jobs:
  analysis:
    name: Scorecard analysis
    runs-on: ubuntu-latest
    permissions:
      # Needed to upload the results to code-scanning dashboard.
      security-events: write
      # Needed to publish results and get a badge (see publish_results below).
      id-token: write

    steps:
      - name: "Checkout code"
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
        with:
          persist-credentials: false

      - name: "Run analysis"
        uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3
        with:
          results_file: results.sarif
          results_format: sarif
          publish_results: true

      # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
      # format to the repository Actions tab.
      - name: "Upload artifact"
        uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
        with:
          name: SARIF file
          path: results.sarif
          retention-days: 5

      # Upload the results to GitHub's code scanning dashboard.
      - name: "Upload to code-scanning"
        uses: github/codeql-action/upload-sarif@b1bff81932f5cdfc8695c7752dcee935dcd061c8 # v4.33.0
        with:
          sarif_file: results.sarif


================================================
FILE: .github/workflows/test.yml
================================================
on: [push, pull_request]
name: Test
permissions:
  contents: read

jobs:
  test:
    name: Test Go ${{ matrix.go }}
    runs-on: ubuntu-latest
    strategy:
      matrix:
        go: ['1.26', '1.25']
    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
      - name: Setup Go
        uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
        with:
          go-version: ${{ matrix.go }}
      - name: Test
        run: go test ./...


================================================
FILE: .gitignore
================================================
*.test


================================================
FILE: LICENSE
================================================

                                 Apache License
                           Version 2.0, January 2004
                        http://www.apache.org/licenses/

   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

   1. Definitions.

      "License" shall mean the terms and conditions for use, reproduction,
      and distribution as defined by Sections 1 through 9 of this document.

      "Licensor" shall mean the copyright owner or entity authorized by
      the copyright owner that is granting the License.

      "Legal Entity" shall mean the union of the acting entity and all
      other entities that control, are controlled by, or are under common
      control with that entity. For the purposes of this definition,
      "control" means (i) the power, direct or indirect, to cause the
      direction or management of such entity, whether by contract or
      otherwise, or (ii) ownership of fifty percent (50%) or more of the
      outstanding shares, or (iii) beneficial ownership of such entity.

      "You" (or "Your") shall mean an individual or Legal Entity
      exercising permissions granted by this License.

      "Source" form shall mean the preferred form for making modifications,
      including but not limited to software source code, documentation
      source, and configuration files.

      "Object" form shall mean any form resulting from mechanical
      transformation or translation of a Source form, including but
      not limited to compiled object code, generated documentation,
      and conversions to other media types.

      "Work" shall mean the work of authorship, whether in Source or
      Object form, made available under the License, as indicated by a
      copyright notice that is included in or attached to the work
      (an example is provided in the Appendix below).

      "Derivative Works" shall mean any work, whether in Source or Object
      form, that is based on (or derived from) the Work and for which the
      editorial revisions, annotations, elaborations, or other modifications
      represent, as a whole, an original work of authorship. For the purposes
      of this License, Derivative Works shall not include works that remain
      separable from, or merely link (or bind by name) to the interfaces of,
      the Work and Derivative Works thereof.

      "Contribution" shall mean any work of authorship, including
      the original version of the Work and any modifications or additions
      to that Work or Derivative Works thereof, that is intentionally
      submitted to Licensor for inclusion in the Work by the copyright owner
      or by an individual or Legal Entity authorized to submit on behalf of
      the copyright owner. For the purposes of this definition, "submitted"
      means any form of electronic, verbal, or written communication sent
      to the Licensor or its representatives, including but not limited to
      communication on electronic mailing lists, source code control systems,
      and issue tracking systems that are managed by, or on behalf of, the
      Licensor for the purpose of discussing and improving the Work, but
      excluding communication that is conspicuously marked or otherwise
      designated in writing by the copyright owner as "Not a Contribution."

      "Contributor" shall mean Licensor and any individual or Legal Entity
      on behalf of whom a Contribution has been received by Licensor and
      subsequently incorporated within the Work.

   2. Grant of Copyright License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      copyright license to reproduce, prepare Derivative Works of,
      publicly display, publicly perform, sublicense, and distribute the
      Work and such Derivative Works in Source or Object form.

   3. Grant of Patent License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      (except as stated in this section) patent license to make, have made,
      use, offer to sell, sell, import, and otherwise transfer the Work,
      where such license applies only to those patent claims licensable
      by such Contributor that are necessarily infringed by their
      Contribution(s) alone or by combination of their Contribution(s)
      with the Work to which such Contribution(s) was submitted. If You
      institute patent litigation against any entity (including a
      cross-claim or counterclaim in a lawsuit) alleging that the Work
      or a Contribution incorporated within the Work constitutes direct
      or contributory patent infringement, then any patent licenses
      granted to You under this License for that Work shall terminate
      as of the date such litigation is filed.

   4. Redistribution. You may reproduce and distribute copies of the
      Work or Derivative Works thereof in any medium, with or without
      modifications, and in Source or Object form, provided that You
      meet the following conditions:

      (a) You must give any other recipients of the Work or
          Derivative Works a copy of this License; and

      (b) You must cause any modified files to carry prominent notices
          stating that You changed the files; and

      (c) You must retain, in the Source form of any Derivative Works
          that You distribute, all copyright, patent, trademark, and
          attribution notices from the Source form of the Work,
          excluding those notices that do not pertain to any part of
          the Derivative Works; and

      (d) If the Work includes a "NOTICE" text file as part of its
          distribution, then any Derivative Works that You distribute must
          include a readable copy of the attribution notices contained
          within such NOTICE file, excluding those notices that do not
          pertain to any part of the Derivative Works, in at least one
          of the following places: within a NOTICE text file distributed
          as part of the Derivative Works; within the Source form or
          documentation, if provided along with the Derivative Works; or,
          within a display generated by the Derivative Works, if and
          wherever such third-party notices normally appear. The contents
          of the NOTICE file are for informational purposes only and
          do not modify the License. You may add Your own attribution
          notices within Derivative Works that You distribute, alongside
          or as an addendum to the NOTICE text from the Work, provided
          that such additional attribution notices cannot be construed
          as modifying the License.

      You may add Your own copyright statement to Your modifications and
      may provide additional or different license terms and conditions
      for use, reproduction, or distribution of Your modifications, or
      for any such Derivative Works as a whole, provided Your use,
      reproduction, and distribution of the Work otherwise complies with
      the conditions stated in this License.

   5. Submission of Contributions. Unless You explicitly state otherwise,
      any Contribution intentionally submitted for inclusion in the Work
      by You to the Licensor shall be under the terms and conditions of
      this License, without any additional terms or conditions.
      Notwithstanding the above, nothing herein shall supersede or modify
      the terms of any separate license agreement you may have executed
      with Licensor regarding such Contributions.

   6. Trademarks. This License does not grant permission to use the trade
      names, trademarks, service marks, or product names of the Licensor,
      except as required for reasonable and customary use in describing the
      origin of the Work and reproducing the content of the NOTICE file.

   7. Disclaimer of Warranty. Unless required by applicable law or
      agreed to in writing, Licensor provides the Work (and each
      Contributor provides its Contributions) on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
      implied, including, without limitation, any warranties or conditions
      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
      PARTICULAR PURPOSE. You are solely responsible for determining the
      appropriateness of using or redistributing the Work and assume any
      risks associated with Your exercise of permissions under this License.

   8. Limitation of Liability. In no event and under no legal theory,
      whether in tort (including negligence), contract, or otherwise,
      unless required by applicable law (such as deliberate and grossly
      negligent acts) or agreed to in writing, shall any Contributor be
      liable to You for damages, including any direct, indirect, special,
      incidental, or consequential damages of any character arising as a
      result of this License or out of the use or inability to use the
      Work (including but not limited to damages for loss of goodwill,
      work stoppage, computer failure or malfunction, or any and all
      other commercial damages or losses), even if such Contributor
      has been advised of the possibility of such damages.

   9. Accepting Warranty or Additional Liability. While redistributing
      the Work or Derivative Works thereof, You may choose to offer,
      and charge a fee for, acceptance of support, warranty, indemnity,
      or other liability obligations and/or rights consistent with this
      License. However, in accepting such obligations, You may act only
      on Your own behalf and on Your sole responsibility, not on behalf
      of any other Contributor, and only if You agree to indemnify,
      defend, and hold each Contributor harmless for any liability
      incurred by, or claims asserted against, such Contributor by reason
      of your accepting any such warranty or additional liability.


================================================
FILE: README.md
================================================
# gosaml2

[![Build Status](https://github.com/russellhaering/gosaml2/actions/workflows/test.yml/badge.svg?branch=main)](https://github.com/russellhaering/gosaml2/actions/workflows/test.yml?query=branch%3Amain)
[![GoDoc](https://godoc.org/github.com/russellhaering/gosaml2?status.svg)](https://godoc.org/github.com/russellhaering/gosaml2)

SAML 2.0 implemementation for Service Providers based on [etree](https://github.com/beevik/etree)
and [goxmldsig](https://github.com/russellhaering/goxmldsig), a pure Go
implementation of XML digital signatures.

## Installation

Install `gosaml2` into your `$GOPATH` using `go get`:

```
go get github.com/russellhaering/gosaml2
```

## Example

See [demo.go](s2example/demo.go).

## Supported Identity Providers

This library is meant to be a generic SAML implementation. If you find a
standards compliant identity provider that it doesn't work with please
submit a bug or pull request.

The following identity providers have been tested:

* Okta
* Auth0
* Shibboleth
* Ipsilon
* OneLogin
* Azure Active Directory (Azure AD)


================================================
FILE: SECURITY.md
================================================
# Security Policy

## Reporting a Vulnerability

Security vulnerabilities can be reported using GitHub's [private vulnerability reporting tool](https://github.com/russellhaering/gosaml2/security/advisories/new).


================================================
FILE: attribute.go
================================================
// Copyright 2016 Russell Haering et al.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package saml2

import "github.com/russellhaering/gosaml2/types"

// Values is a convenience wrapper for a map of strings to Attributes, which
// can be used for easy access to the string values of Attribute lists.
type Values map[string]types.Attribute

// Get is a safe method (nil maps will not panic) for returning the first value
// for an attribute at a key, or the empty string if none exists.
func (vals Values) Get(k string) string {
	if vals == nil {
		return ""
	}
	if v, ok := vals[k]; ok && len(v.Values) > 0 {
		return string(v.Values[0].Value)
	}
	return ""
}

//GetSize returns the number of values for an attribute at a key.
//Returns '0' in case of error or if key is not found.
func (vals Values) GetSize(k string) int {
	if vals == nil {
		return 0
	}

	v, ok := vals[k]
	if ok {
		return len(v.Values)
	}

	return 0
}

//GetAll returns all the values for an attribute at a key.
//Returns an empty slice in case of error of if key is not found.
func (vals Values) GetAll(k string) []string {
	var av []string

	if vals == nil {
		return av
	}

	if v, ok := vals[k]; ok && len(v.Values) > 0 {
		for i := 0; i < len(v.Values); i++ {
			av = append(av, string(v.Values[i].Value))
		}
	}

	return av
}


================================================
FILE: authn_request.go
================================================
// Copyright 2016 Russell Haering et al.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package saml2

import "time"

// AuthNRequest is the go struct representation of an authentication request
type AuthNRequest struct {
	ID                          string `xml:",attr"`
	Version                     string `xml:",attr"`
	ProtocolBinding             string `xml:",attr"`
	AssertionConsumerServiceURL string `xml:",attr"`

	IssueInstant time.Time `xml:",attr"`

	Destination string `xml:",attr"`
	Issuer      string
}


================================================
FILE: build_logout_response.go
================================================
// Copyright 2016 Russell Haering et al.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package saml2

import (
	"bytes"
	"encoding/base64"
	"html/template"

	"github.com/beevik/etree"
	"github.com/russellhaering/gosaml2/uuid"
)

func (sp *SAMLServiceProvider) buildLogoutResponse(statusCodeValue string, reqID string, includeSig bool) (*etree.Document, error) {
	logoutResponse := &etree.Element{
		Space: "samlp",
		Tag:   "LogoutResponse",
	}

	logoutResponse.CreateAttr("xmlns:samlp", "urn:oasis:names:tc:SAML:2.0:protocol")
	logoutResponse.CreateAttr("xmlns:saml", "urn:oasis:names:tc:SAML:2.0:assertion")

	arId := uuid.NewV4()

	logoutResponse.CreateAttr("ID", "_"+arId.String())
	logoutResponse.CreateAttr("Version", "2.0")
	logoutResponse.CreateAttr("IssueInstant", sp.Clock.Now().UTC().Format(issueInstantFormat))
	logoutResponse.CreateAttr("Destination", sp.IdentityProviderSLOURL)
	logoutResponse.CreateAttr("InResponseTo", reqID)

	// NOTE(russell_h): In earlier versions we mistakenly sent the IdentityProviderIssuer
	// in the AuthnRequest. For backwards compatibility we will fall back to that
	// behavior when ServiceProviderIssuer isn't set.
	if sp.ServiceProviderIssuer != "" {
		logoutResponse.CreateElement("saml:Issuer").SetText(sp.ServiceProviderIssuer)
	} else {
		logoutResponse.CreateElement("saml:Issuer").SetText(sp.IdentityProviderIssuer)
	}

	status := logoutResponse.CreateElement("samlp:Status")
	statusCode := status.CreateElement("samlp:StatusCode")
	statusCode.CreateAttr("Value", statusCodeValue)

	doc := etree.NewDocument()

	// Only POST binding includes <Signature> in <AuthnRequest> (includeSig)
	if includeSig {
		signed, err := sp.SignLogoutResponse(logoutResponse)
		if err != nil {
			return nil, err
		}

		doc.SetRoot(signed)
	} else {
		doc.SetRoot(logoutResponse)
	}
	return doc, nil
}
func (sp *SAMLServiceProvider) BuildLogoutResponseDocument(status string, reqID string) (*etree.Document, error) {
	return sp.buildLogoutResponse(status, reqID, true)
}

func (sp *SAMLServiceProvider) BuildLogoutResponseDocumentNoSig(status string, reqID string) (*etree.Document, error) {
	return sp.buildLogoutResponse(status, reqID, false)
}

func (sp *SAMLServiceProvider) SignLogoutResponse(el *etree.Element) (*etree.Element, error) {
	ctx := sp.SigningContext()

	sig, err := ctx.ConstructSignature(el, true)
	if err != nil {
		return nil, err
	}

	ret := el.Copy()

	var children []etree.Token
	children = append(children, ret.Child[0])     // issuer is always first
	children = append(children, sig)              // next is the signature
	children = append(children, ret.Child[1:]...) // then all other children
	ret.Child = children

	return ret, nil
}

func (sp *SAMLServiceProvider) buildLogoutResponseBodyPostFromDocument(relayState string, doc *etree.Document) ([]byte, error) {
	respBuf, err := doc.WriteToBytes()
	if err != nil {
		return nil, err
	}

	encodedRespBuf := base64.StdEncoding.EncodeToString(respBuf)

	var tmpl *template.Template
	var rv bytes.Buffer

	if relayState != "" {
		tmpl = template.Must(template.New("saml-post-form").Parse(`<html>` +
			`<form method="post" action="{{.URL}}" id="SAMLResponseForm">` +
			`<input type="hidden" name="SAMLResponse" value="{{.SAMLResponse}}" />` +
			`<input type="hidden" name="RelayState" value="{{.RelayState}}" />` +
			`<input id="SAMLSubmitButton" type="submit" value="Continue" />` +
			`</form>` +
			`<script>document.getElementById('SAMLSubmitButton').style.visibility='hidden';</script>` +
			`<script>document.getElementById('SAMLResponseForm').submit();</script>` +
			`</html>`))
		data := struct {
			URL          string
			SAMLResponse string
			RelayState   string
		}{
			URL:          sp.IdentityProviderSLOURL,
			SAMLResponse: encodedRespBuf,
			RelayState:   relayState,
		}
		if err = tmpl.Execute(&rv, data); err != nil {
			return nil, err
		}
	} else {
		tmpl = template.Must(template.New("saml-post-form").Parse(`<html>` +
			`<form method="post" action="{{.URL}}" id="SAMLResponseForm">` +
			`<input type="hidden" name="SAMLResponse" value="{{.SAMLResponse}}" />` +
			`<input id="SAMLSubmitButton" type="submit" value="Continue" />` +
			`</form>` +
			`<script>document.getElementById('SAMLSubmitButton').style.visibility='hidden';</script>` +
			`<script>document.getElementById('SAMLResponseForm').submit();</script>` +
			`</html>`))
		data := struct {
			URL          string
			SAMLResponse string
		}{
			URL:          sp.IdentityProviderSLOURL,
			SAMLResponse: encodedRespBuf,
		}

		if err = tmpl.Execute(&rv, data); err != nil {
			return nil, err
		}
	}

	return rv.Bytes(), nil
}

func (sp *SAMLServiceProvider) BuildLogoutResponseBodyPostFromDocument(relayState string, doc *etree.Document) ([]byte, error) {
	return sp.buildLogoutResponseBodyPostFromDocument(relayState, doc)
}


================================================
FILE: build_request.go
================================================
// Copyright 2016 Russell Haering et al.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package saml2

import (
	"bytes"
	"compress/flate"
	"encoding/base64"
	"fmt"
	"html/template"
	"net/http"
	"net/url"

	"github.com/beevik/etree"
	"github.com/russellhaering/gosaml2/uuid"
)

const issueInstantFormat = "2006-01-02T15:04:05Z"

func (sp *SAMLServiceProvider) buildAuthnRequest(includeSig bool) (*etree.Document, error) {
	authnRequest := &etree.Element{
		Space: "samlp",
		Tag:   "AuthnRequest",
	}

	authnRequest.CreateAttr("xmlns:samlp", "urn:oasis:names:tc:SAML:2.0:protocol")
	authnRequest.CreateAttr("xmlns:saml", "urn:oasis:names:tc:SAML:2.0:assertion")

	arId := uuid.NewV4()

	authnRequest.CreateAttr("ID", "_"+arId.String())
	authnRequest.CreateAttr("Version", "2.0")
	authnRequest.CreateAttr("ProtocolBinding", "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST")
	authnRequest.CreateAttr("AssertionConsumerServiceURL", sp.AssertionConsumerServiceURL)
	authnRequest.CreateAttr("IssueInstant", sp.Clock.Now().UTC().Format(issueInstantFormat))
	authnRequest.CreateAttr("Destination", sp.IdentityProviderSSOURL)
	if sp.ForceAuthn {
		authnRequest.CreateAttr("ForceAuthn", "true")
	}
	if sp.IsPassive {
		authnRequest.CreateAttr("IsPassive", "true")
	}

	// NOTE(russell_h): In earlier versions we mistakenly sent the IdentityProviderIssuer
	// in the AuthnRequest. For backwards compatibility we will fall back to that
	// behavior when ServiceProviderIssuer isn't set.
	if sp.ServiceProviderIssuer != "" {
		authnRequest.CreateElement("saml:Issuer").SetText(sp.ServiceProviderIssuer)
	} else {
		authnRequest.CreateElement("saml:Issuer").SetText(sp.IdentityProviderIssuer)
	}

	nameIdPolicy := authnRequest.CreateElement("samlp:NameIDPolicy")
	nameIdPolicy.CreateAttr("AllowCreate", "true")
	if sp.NameIdFormat != "" {
		nameIdPolicy.CreateAttr("Format", sp.NameIdFormat)
	}

	if sp.RequestedAuthnContext != nil {
		requestedAuthnContext := authnRequest.CreateElement("samlp:RequestedAuthnContext")
		requestedAuthnContext.CreateAttr("Comparison", sp.RequestedAuthnContext.Comparison)

		for _, context := range sp.RequestedAuthnContext.Contexts {
			authnContextClassRef := requestedAuthnContext.CreateElement("saml:AuthnContextClassRef")
			authnContextClassRef.SetText(context)
		}
	}

	doc := etree.NewDocument()

	// Only POST binding includes <Signature> in <AuthnRequest> (includeSig)
	if sp.SignAuthnRequests && includeSig {
		signed, err := sp.SignAuthnRequest(authnRequest)
		if err != nil {
			return nil, err
		}

		doc.SetRoot(signed)
	} else {
		doc.SetRoot(authnRequest)
	}
	return doc, nil
}

func (sp *SAMLServiceProvider) BuildAuthRequestDocument() (*etree.Document, error) {
	return sp.buildAuthnRequest(true)
}

func (sp *SAMLServiceProvider) BuildAuthRequestDocumentNoSig() (*etree.Document, error) {
	return sp.buildAuthnRequest(false)
}

// SignAuthnRequest takes a document, builds a signature, creates another document
// and inserts the signature in it. According to the schema, the position of the
// signature is right after the Issuer [1] then all other children.
//
// [1] https://docs.oasis-open.org/security/saml/v2.0/saml-schema-protocol-2.0.xsd
func (sp *SAMLServiceProvider) SignAuthnRequest(el *etree.Element) (*etree.Element, error) {
	ctx := sp.SigningContext()

	sig, err := ctx.ConstructSignature(el, true)
	if err != nil {
		return nil, err
	}

	ret := el.Copy()

	var children []etree.Token
	children = append(children, ret.Child[0])     // issuer is always first
	children = append(children, sig)              // next is the signature
	children = append(children, ret.Child[1:]...) // then all other children
	ret.Child = children

	return ret, nil
}

// BuildAuthRequest builds <AuthnRequest> for identity provider
func (sp *SAMLServiceProvider) BuildAuthRequest() (string, error) {
	doc, err := sp.BuildAuthRequestDocument()
	if err != nil {
		return "", err
	}
	return doc.WriteToString()
}

func (sp *SAMLServiceProvider) buildAuthURLFromDocument(relayState, binding string, doc *etree.Document) (string, error) {
	parsedUrl, err := url.Parse(sp.IdentityProviderSSOURL)
	if err != nil {
		return "", err
	}

	authnRequest, err := doc.WriteToString()
	if err != nil {
		return "", err
	}

	buf := &bytes.Buffer{}

	fw, err := flate.NewWriter(buf, flate.DefaultCompression)
	if err != nil {
		return "", fmt.Errorf("flate NewWriter error: %v", err)
	}

	_, err = fw.Write([]byte(authnRequest))
	if err != nil {
		return "", fmt.Errorf("flate.Writer Write error: %v", err)
	}

	err = fw.Close()
	if err != nil {
		return "", fmt.Errorf("flate.Writer Close error: %v", err)
	}

	qs := parsedUrl.Query()

	qs.Add("SAMLRequest", base64.StdEncoding.EncodeToString(buf.Bytes()))

	if relayState != "" {
		qs.Add("RelayState", relayState)
	}

	if sp.SignAuthnRequests && binding == BindingHttpRedirect {
		// Sign URL encoded query (see Section 3.4.4.1 DEFLATE Encoding of saml-bindings-2.0-os.pdf)
		ctx := sp.SigningContext()
		qs.Add("SigAlg", ctx.GetSignatureMethodIdentifier())
		var rawSignature []byte
		if rawSignature, err = ctx.SignString(signatureInputString(qs.Get("SAMLRequest"), qs.Get("RelayState"), qs.Get("SigAlg"))); err != nil {
			return "", fmt.Errorf("unable to sign query string of redirect URL: %v", err)
		}

		// Now add base64 encoded Signature
		qs.Add("Signature", base64.StdEncoding.EncodeToString(rawSignature))
	}

	//Here the parameters may appear in any order.
	parsedUrl.RawQuery = qs.Encode()
	return parsedUrl.String(), nil
}

func (sp *SAMLServiceProvider) BuildAuthURLFromDocument(relayState string, doc *etree.Document) (string, error) {
	return sp.buildAuthURLFromDocument(relayState, BindingHttpPost, doc)
}

func (sp *SAMLServiceProvider) BuildAuthURLRedirect(relayState string, doc *etree.Document) (string, error) {
	return sp.buildAuthURLFromDocument(relayState, BindingHttpRedirect, doc)
}

func (sp *SAMLServiceProvider) buildAuthBodyPostFromDocument(relayState string, doc *etree.Document) ([]byte, error) {
	reqBuf, err := doc.WriteToBytes()
	if err != nil {
		return nil, err
	}

	encodedReqBuf := base64.StdEncoding.EncodeToString(reqBuf)

	var tmpl *template.Template
	var rv bytes.Buffer

	if relayState != "" {
		tmpl = template.Must(template.New("saml-post-form").Parse(`` +
			`<form method="POST" action="{{.URL}}" id="SAMLRequestForm">` +
			`<input type="hidden" name="SAMLRequest" value="{{.SAMLRequest}}" />` +
			`<input type="hidden" name="RelayState" value="{{.RelayState}}" />` +
			`<input id="SAMLSubmitButton" type="submit" value="Submit" />` +
			`</form>` +
			`<script>document.getElementById('SAMLSubmitButton').style.visibility="hidden";` +
			`document.getElementById('SAMLRequestForm').submit();</script>`))

		data := struct {
			URL         string
			SAMLRequest string
			RelayState  string
		}{
			URL:         sp.IdentityProviderSSOURL,
			SAMLRequest: encodedReqBuf,
			RelayState:  relayState,
		}
		if err = tmpl.Execute(&rv, data); err != nil {
			return nil, err
		}
	} else {
		tmpl = template.Must(template.New("saml-post-form").Parse(`` +
			`<form method="POST" action="{{.URL}}" id="SAMLRequestForm">` +
			`<input type="hidden" name="SAMLRequest" value="{{.SAMLRequest}}" />` +
			`<input id="SAMLSubmitButton" type="submit" value="Submit" />` +
			`</form>` +
			`<script>document.getElementById('SAMLSubmitButton').style.visibility="hidden";` +
			`document.getElementById('SAMLRequestForm').submit();</script>`))

		data := struct {
			URL         string
			SAMLRequest string
		}{
			URL:         sp.IdentityProviderSSOURL,
			SAMLRequest: encodedReqBuf,
		}
		if err = tmpl.Execute(&rv, data); err != nil {
			return nil, err
		}
	}

	return rv.Bytes(), nil
}

//BuildAuthBodyPost builds the POST body to be sent to IDP.
func (sp *SAMLServiceProvider) BuildAuthBodyPost(relayState string) ([]byte, error) {
	var doc *etree.Document
	var err error

	if sp.SignAuthnRequests {
		doc, err = sp.BuildAuthRequestDocument()
	} else {
		doc, err = sp.BuildAuthRequestDocumentNoSig()
	}

	if err != nil {
		return nil, err
	}

	return sp.buildAuthBodyPostFromDocument(relayState, doc)
}

//BuildAuthBodyPostFromDocument builds the POST body to be sent to IDP.
//It takes the AuthnRequest xml as input.
func (sp *SAMLServiceProvider) BuildAuthBodyPostFromDocument(relayState string, doc *etree.Document) ([]byte, error) {
	return sp.buildAuthBodyPostFromDocument(relayState, doc)
}

// BuildAuthURL builds redirect URL to be sent to principal
func (sp *SAMLServiceProvider) BuildAuthURL(relayState string) (string, error) {
	doc, err := sp.BuildAuthRequestDocument()
	if err != nil {
		return "", err
	}
	return sp.BuildAuthURLFromDocument(relayState, doc)
}

// AuthRedirect takes a ResponseWriter and Request from an http interaction and
// redirects to the SAMLServiceProvider's configured IdP, including the
// relayState provided, if any.
func (sp *SAMLServiceProvider) AuthRedirect(w http.ResponseWriter, r *http.Request, relayState string) (err error) {
	url, err := sp.BuildAuthURL(relayState)
	if err != nil {
		return err
	}

	http.Redirect(w, r, url, http.StatusFound)
	return nil
}

func (sp *SAMLServiceProvider) buildLogoutRequest(includeSig bool, nameID string, sessionIndex string) (*etree.Document, error) {
	logoutRequest := &etree.Element{
		Space: "samlp",
		Tag:   "LogoutRequest",
	}

	logoutRequest.CreateAttr("xmlns:samlp", "urn:oasis:names:tc:SAML:2.0:protocol")
	logoutRequest.CreateAttr("xmlns:saml", "urn:oasis:names:tc:SAML:2.0:assertion")

	arId := uuid.NewV4()

	logoutRequest.CreateAttr("ID", "_"+arId.String())
	logoutRequest.CreateAttr("Version", "2.0")
	logoutRequest.CreateAttr("IssueInstant", sp.Clock.Now().UTC().Format(issueInstantFormat))
	logoutRequest.CreateAttr("Destination", sp.IdentityProviderSLOURL)

	// NOTE(russell_h): In earlier versions we mistakenly sent the IdentityProviderIssuer
	// in the AuthnRequest. For backwards compatibility we will fall back to that
	// behavior when ServiceProviderIssuer isn't set.
	// TODO: Throw error in case Issuer is empty.
	if sp.ServiceProviderIssuer != "" {
		logoutRequest.CreateElement("saml:Issuer").SetText(sp.ServiceProviderIssuer)
	} else {
		logoutRequest.CreateElement("saml:Issuer").SetText(sp.IdentityProviderIssuer)
	}

	nameId := logoutRequest.CreateElement("saml:NameID")
	nameId.SetText(nameID)
	nameId.CreateAttr("Format", sp.NameIdFormat)

	//Section 3.7.1 - http://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf says
	//SessionIndex is optional. If the IDP supports SLO then it must send SessionIndex as per
	//Section 4.1.4.2 of https://docs.oasis-open.org/security/saml/v2.0/saml-profiles-2.0-os.pdf.
	//As per section 4.4.3.1 of //docs.oasis-open.org/security/saml/v2.0/saml-profiles-2.0-os.pdf,
	//a LogoutRequest issued by Session Participant to Identity Provider, must contain
	//at least one SessionIndex element needs to be included.
	nameId = logoutRequest.CreateElement("samlp:SessionIndex")
	nameId.SetText(sessionIndex)

	doc := etree.NewDocument()

	if includeSig {
		signed, err := sp.SignLogoutRequest(logoutRequest)
		if err != nil {
			return nil, err
		}

		doc.SetRoot(signed)
	} else {
		doc.SetRoot(logoutRequest)
	}

	return doc, nil
}

func (sp *SAMLServiceProvider) SignLogoutRequest(el *etree.Element) (*etree.Element, error) {
	ctx := sp.SigningContext()

	sig, err := ctx.ConstructSignature(el, true)
	if err != nil {
		return nil, err
	}

	ret := el.Copy()

	var children []etree.Token
	children = append(children, ret.Child[0])     // issuer is always first
	children = append(children, sig)              // next is the signature
	children = append(children, ret.Child[1:]...) // then all other children
	ret.Child = children

	return ret, nil
}

func (sp *SAMLServiceProvider) BuildLogoutRequestDocumentNoSig(nameID string, sessionIndex string) (*etree.Document, error) {
	return sp.buildLogoutRequest(false, nameID, sessionIndex)
}

func (sp *SAMLServiceProvider) BuildLogoutRequestDocument(nameID string, sessionIndex string) (*etree.Document, error) {
	return sp.buildLogoutRequest(true, nameID, sessionIndex)
}

//BuildLogoutBodyPostFromDocument builds the POST body to be sent to IDP.
//It takes the LogoutRequest xml as input.
func (sp *SAMLServiceProvider) BuildLogoutBodyPostFromDocument(relayState string, doc *etree.Document) ([]byte, error) {
	return sp.buildLogoutBodyPostFromDocument(relayState, doc)
}

func (sp *SAMLServiceProvider) buildLogoutBodyPostFromDocument(relayState string, doc *etree.Document) ([]byte, error) {
	reqBuf, err := doc.WriteToBytes()
	if err != nil {
		return nil, err
	}

	encodedReqBuf := base64.StdEncoding.EncodeToString(reqBuf)
	var tmpl *template.Template
	var rv bytes.Buffer

	if relayState != "" {
		tmpl = template.Must(template.New("saml-post-form").Parse(`` +
			`<form method="POST" action="{{.URL}}" id="SAMLRequestForm">` +
			`<input type="hidden" name="SAMLRequest" value="{{.SAMLRequest}}" />` +
			`<input type="hidden" name="RelayState" value="{{.RelayState}}" />` +
			`<input id="SAMLSubmitButton" type="submit" value="Submit" />` +
			`</form>` +
			`<script>document.getElementById('SAMLSubmitButton').style.visibility="hidden";` +
			`document.getElementById('SAMLRequestForm').submit();</script>`))

		data := struct {
			URL         string
			SAMLRequest string
			RelayState  string
		}{
			URL:         sp.IdentityProviderSLOURL,
			SAMLRequest: encodedReqBuf,
			RelayState:  relayState,
		}
		if err = tmpl.Execute(&rv, data); err != nil {
			return nil, err
		}
	} else {
		tmpl = template.Must(template.New("saml-post-form").Parse(`` +
			`<form method="POST" action="{{.URL}}" id="SAMLRequestForm">` +
			`<input type="hidden" name="SAMLRequest" value="{{.SAMLRequest}}" />` +
			`<input id="SAMLSubmitButton" type="submit" value="Submit" />` +
			`</form>` +
			`<script>document.getElementById('SAMLSubmitButton').style.visibility="hidden";` +
			`document.getElementById('SAMLRequestForm').submit();</script>`))

		data := struct {
			URL         string
			SAMLRequest string
		}{
			URL:         sp.IdentityProviderSLOURL,
			SAMLRequest: encodedReqBuf,
		}
		if err = tmpl.Execute(&rv, data); err != nil {
			return nil, err
		}
	}

	return rv.Bytes(), nil
}

func (sp *SAMLServiceProvider) BuildLogoutURLRedirect(relayState string, doc *etree.Document) (string, error) {
	return sp.buildLogoutURLFromDocument(relayState, BindingHttpRedirect, doc)
}

func (sp *SAMLServiceProvider) buildLogoutURLFromDocument(relayState, binding string, doc *etree.Document) (string, error) {
	parsedUrl, err := url.Parse(sp.IdentityProviderSLOURL)
	if err != nil {
		return "", err
	}

	logoutRequest, err := doc.WriteToString()
	if err != nil {
		return "", err
	}

	buf := &bytes.Buffer{}

	fw, err := flate.NewWriter(buf, flate.DefaultCompression)
	if err != nil {
		return "", fmt.Errorf("flate NewWriter error: %v", err)
	}

	_, err = fw.Write([]byte(logoutRequest))
	if err != nil {
		return "", fmt.Errorf("flate.Writer Write error: %v", err)
	}

	err = fw.Close()
	if err != nil {
		return "", fmt.Errorf("flate.Writer Close error: %v", err)
	}

	qs := parsedUrl.Query()

	qs.Add("SAMLRequest", base64.StdEncoding.EncodeToString(buf.Bytes()))

	if relayState != "" {
		qs.Add("RelayState", relayState)
	}

	if binding == BindingHttpRedirect {
		// Sign URL encoded query (see Section 3.4.4.1 DEFLATE Encoding of saml-bindings-2.0-os.pdf)
		ctx := sp.SigningContext()
		qs.Add("SigAlg", ctx.GetSignatureMethodIdentifier())
		var rawSignature []byte
		//qs.Encode() sorts the keys (See https://golang.org/pkg/net/url/#Values.Encode).
		//If RelayState parameter is present then RelayState parameter
		//will be put first by Encode(). Hence encode them separately and concatenate.
		//Signature string has to have parameters in the order - SAMLRequest=value&RelayState=value&SigAlg=value.
		//(See Section 3.4.4.1 saml-bindings-2.0-os.pdf).
		var orderedParams = []string{"SAMLRequest", "RelayState", "SigAlg"}

		var paramValueMap = make(map[string]string)
		paramValueMap["SAMLRequest"] = base64.StdEncoding.EncodeToString(buf.Bytes())
		if relayState != "" {
			paramValueMap["RelayState"] = relayState
		}
		paramValueMap["SigAlg"] = ctx.GetSignatureMethodIdentifier()

		ss := ""

		for _, k := range orderedParams {
			v, ok := paramValueMap[k]
			if ok {
				//Add the value after URL encoding.
				u := url.Values{}
				u.Add(k, v)
				e := u.Encode()
				if ss != "" {
					ss += "&" + e
				} else {
					ss = e
				}
			}
		}

		//Now generate the signature on the string of ordered parameters.
		if rawSignature, err = ctx.SignString(ss); err != nil {
			return "", fmt.Errorf("unable to sign query string of redirect URL: %v", err)
		}

		// Now add base64 encoded Signature
		qs.Add("Signature", base64.StdEncoding.EncodeToString(rawSignature))
	}

	//Here the parameters may appear in any order.
	parsedUrl.RawQuery = qs.Encode()
	return parsedUrl.String(), nil
}

// signatureInputString constructs the string to be fed into the signature algorithm, as described
// in section 3.4.4.1 of
// https://www.oasis-open.org/committees/download.php/56779/sstc-saml-bindings-errata-2.0-wd-06.pdf
func signatureInputString(samlRequest, relayState, sigAlg string) string {
	var params [][2]string
	if relayState == "" {
		params = [][2]string{{"SAMLRequest", samlRequest}, {"SigAlg", sigAlg}}
	} else {
		params = [][2]string{{"SAMLRequest", samlRequest}, {"RelayState", relayState}, {"SigAlg", sigAlg}}
	}

	var buf bytes.Buffer
	for _, kv := range params {
		k, v := kv[0], kv[1]
		if buf.Len() > 0 {
			buf.WriteByte('&')
		}
		buf.WriteString(url.QueryEscape(k) + "=" + url.QueryEscape(v))
	}
	return buf.String()
}


================================================
FILE: build_request_test.go
================================================
// Copyright 2016 Russell Haering et al.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package saml2

import (
	"bytes"
	"compress/flate"
	"encoding/base64"
	"encoding/xml"
	"fmt"
	"net/http"
	"net/http/httptest"
	"net/url"
	"testing"
	"time"

	"github.com/beevik/etree"
	"github.com/stretchr/testify/require"
)

func TestRedirect(t *testing.T) {
	r := &http.Request{URL: &url.URL{Path: "/"}}
	w := httptest.NewRecorder()

	spURL := "https://sp.test"

	sp := SAMLServiceProvider{
		AssertionConsumerServiceURL: spURL,
		AudienceURI:                 spURL,
		IdentityProviderIssuer:      spURL,
		IdentityProviderSSOURL:      "https://idp.test/saml/sso",
		SignAuthnRequests:           false,
	}

	require.NoError(t, sp.AuthRedirect(w, r, "foobar"))
	require.Len(t, w.HeaderMap, 1, "wrong number of headers was set")
	require.Equal(t, http.StatusFound, w.Code, "wrong http status was set")

	u, err := url.Parse(w.HeaderMap.Get("Location"))
	require.NoError(t, err, "invalid url used for redirect")

	require.Equal(t, "idp.test", u.Host)
	require.Equal(t, "https", u.Scheme)
	require.Equal(t, "foobar", u.Query().Get("RelayState"))

	bs, err := base64.StdEncoding.DecodeString(u.Query().Get("SAMLRequest"))
	require.NoError(t, err, "error base64 decoding SAMLRequest query param")

	fr := flate.NewReader(bytes.NewReader(bs))

	req := AuthNRequest{}
	require.NoError(t, xml.NewDecoder(fr).Decode(&req), "Error reading/decoding from flate-compressed URL")

	iss, err := url.Parse(req.Issuer)
	require.NoError(t, err, "error parsing request issuer URL")

	require.Equal(t, "sp.test", iss.Host)
	require.WithinDuration(t, time.Now(), req.IssueInstant, time.Second, "IssueInstant was not within the expected time frame")

	dst, err := url.Parse(req.Destination)
	require.NoError(t, err, "error parsing request destination")
	require.Equal(t, "https", dst.Scheme)
	require.Equal(t, "idp.test", dst.Host)

	//Require that the destination is the same as the redirected URL, except params
	require.Equal(t, fmt.Sprintf("%s://%s%s", u.Scheme, u.Host, u.Path), dst.String())
}

func TestRequestedAuthnContextOmitted(t *testing.T) {
	spURL := "https://sp.test"
	sp := SAMLServiceProvider{
		AssertionConsumerServiceURL: spURL,
		AudienceURI:                 spURL,
		IdentityProviderIssuer:      spURL,
		IdentityProviderSSOURL:      "https://idp.test/saml/sso",
		SignAuthnRequests:           false,
	}

	request, err := sp.BuildAuthRequest()
	require.NoError(t, err)

	doc := etree.NewDocument()
	err = doc.ReadFromString(request)
	require.NoError(t, err)

	el := doc.FindElement("./AuthnRequest/RequestedAuthnContext")
	require.Nil(t, el)
}

func TestRequestedAuthnContextIncluded(t *testing.T) {
	spURL := "https://sp.test"
	sp := SAMLServiceProvider{
		AssertionConsumerServiceURL: spURL,
		AudienceURI:                 spURL,
		IdentityProviderIssuer:      spURL,
		IdentityProviderSSOURL:      "https://idp.test/saml/sso",
		RequestedAuthnContext: &RequestedAuthnContext{
			Comparison: AuthnPolicyMatchExact,
			Contexts: []string{
				AuthnContextPasswordProtectedTransport,
			},
		},
		SignAuthnRequests: false,
	}

	request, err := sp.BuildAuthRequest()
	require.NoError(t, err)

	doc := etree.NewDocument()
	err = doc.ReadFromString(request)
	require.NoError(t, err)

	el := doc.FindElement("./AuthnRequest/RequestedAuthnContext")
	require.Equal(t, el.SelectAttrValue("Comparison", ""), "exact")
	require.Len(t, el.ChildElements(), 1)
	el = el.ChildElements()[0]
	require.Equal(t, el.Tag, "AuthnContextClassRef")
	require.Equal(t, el.Text(), AuthnContextPasswordProtectedTransport)
}

func TestForceAuthnOmitted(t *testing.T) {
	spURL := "https://sp.test"
	sp := SAMLServiceProvider{
		AssertionConsumerServiceURL: spURL,
		AudienceURI:                 spURL,
		IdentityProviderIssuer:      spURL,
		IdentityProviderSSOURL:      "https://idp.test/saml/sso",
	}

	request, err := sp.BuildAuthRequest()
	require.NoError(t, err)

	doc := etree.NewDocument()
	err = doc.ReadFromString(request)
	require.NoError(t, err)

	attr := doc.Root().SelectAttr("ForceAuthn")
	require.Nil(t, attr)
}

func TestForceAuthnIncluded(t *testing.T) {
	spURL := "https://sp.test"
	sp := SAMLServiceProvider{
		AssertionConsumerServiceURL: spURL,
		AudienceURI:                 spURL,
		IdentityProviderIssuer:      spURL,
		IdentityProviderSSOURL:      "https://idp.test/saml/sso",
		ForceAuthn:                  true,
	}

	request, err := sp.BuildAuthRequest()
	require.NoError(t, err)

	doc := etree.NewDocument()
	err = doc.ReadFromString(request)
	require.NoError(t, err)

	attr := doc.Root().SelectAttr("ForceAuthn")
	require.NotNil(t, attr)
	require.Equal(t, "true", attr.Value)
}

func TestIsPassiveOmitted(t *testing.T) {
	spURL := "https://sp.test"
	sp := SAMLServiceProvider{
		AssertionConsumerServiceURL: spURL,
		AudienceURI:                 spURL,
		IdentityProviderIssuer:      spURL,
		IdentityProviderSSOURL:      "https://idp.test/saml/sso",
	}

	request, err := sp.BuildAuthRequest()
	require.NoError(t, err)

	doc := etree.NewDocument()
	err = doc.ReadFromString(request)
	require.NoError(t, err)

	attr := doc.Root().SelectAttr("IsPassive")
	require.Nil(t, attr)
}

func TestIsPassiveIncluded(t *testing.T) {
	spURL := "https://sp.test"
	sp := SAMLServiceProvider{
		AssertionConsumerServiceURL: spURL,
		AudienceURI:                 spURL,
		IdentityProviderIssuer:      spURL,
		IdentityProviderSSOURL:      "https://idp.test/saml/sso",
		IsPassive:                   true,
	}

	request, err := sp.BuildAuthRequest()
	require.NoError(t, err)

	doc := etree.NewDocument()
	err = doc.ReadFromString(request)
	require.NoError(t, err)

	attr := doc.Root().SelectAttr("IsPassive")
	require.NotNil(t, attr)
	require.Equal(t, "true", attr.Value)
}


================================================
FILE: decode_logout_request.go
================================================
// Copyright 2016 Russell Haering et al.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package saml2

import (
	"encoding/base64"
	"fmt"

	dsig "github.com/russellhaering/goxmldsig"
)

func (sp *SAMLServiceProvider) validateLogoutRequestAttributes(request *LogoutRequest) error {
	if request.Destination != "" && request.Destination != sp.ServiceProviderSLOURL {
		return ErrInvalidValue{
			Key:      DestinationAttr,
			Expected: sp.ServiceProviderSLOURL,
			Actual:   request.Destination,
		}
	}

	if request.Version != "2.0" {
		return ErrInvalidValue{
			Reason:   ReasonUnsupported,
			Key:      "SAML version",
			Expected: "2.0",
			Actual:   request.Version,
		}
	}

	return nil
}

func (sp *SAMLServiceProvider) ValidateEncodedLogoutRequestPOST(encodedRequest string) (*LogoutRequest, error) {
	raw, err := base64.StdEncoding.DecodeString(encodedRequest)
	if err != nil {
		return nil, err
	}

	// Parse the raw request - parseResponse is generic
	_, el, err := parseResponse(raw, sp.MaximumDecompressedBodySize)
	if err != nil {
		return nil, err
	}

	var requestSignatureValidated bool
	if !sp.SkipSignatureValidation {
		el, err = sp.validateElementSignature(el)
		if err == dsig.ErrMissingSignature {
			return nil, fmt.Errorf("logout request is not signed")
		} else if err != nil {
			return nil, err
		} else if el == nil {
			return nil, fmt.Errorf("missing transformed logout request")
		} else {
			requestSignatureValidated = true
		}
	}

	decodedRequest := &LogoutRequest{}
	err = xmlUnmarshalElement(el, decodedRequest)
	if err != nil {
		return nil, fmt.Errorf("unable to unmarshal logout request: %v", err)
	}
	decodedRequest.SignatureValidated = requestSignatureValidated

	err = sp.ValidateDecodedLogoutRequest(decodedRequest)
	if err != nil {
		return nil, err
	}

	return decodedRequest, nil
}


================================================
FILE: decode_response.go
================================================
// Copyright 2016 Russell Haering et al.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package saml2

import (
	"bytes"
	"compress/flate"
	"crypto/tls"
	"crypto/x509"
	"encoding/base64"
	"fmt"
	"io"

	"encoding/xml"

	"github.com/beevik/etree"
	rtvalidator "github.com/mattermost/xml-roundtrip-validator"
	"github.com/russellhaering/gosaml2/types"
	dsig "github.com/russellhaering/goxmldsig"
	"github.com/russellhaering/goxmldsig/etreeutils"
)

const (
	defaultMaxDecompressedResponseSize = 5 * 1024 * 1024
)

func (sp *SAMLServiceProvider) validationContext() *dsig.ValidationContext {
	ctx := dsig.NewDefaultValidationContext(sp.IDPCertificateStore)
	ctx.Clock = sp.Clock
	return ctx
}

// validateResponseAttributes validates a SAML Response's tag and attributes. It does
// not inspect child elements of the Response at all.
func (sp *SAMLServiceProvider) validateResponseAttributes(response *types.Response) error {
	if response.Destination != "" && response.Destination != sp.AssertionConsumerServiceURL {
		return ErrInvalidValue{
			Key:      DestinationAttr,
			Expected: sp.AssertionConsumerServiceURL,
			Actual:   response.Destination,
		}
	}

	if response.Version != "2.0" {
		return ErrInvalidValue{
			Reason:   ReasonUnsupported,
			Key:      "SAML version",
			Expected: "2.0",
			Actual:   response.Version,
		}
	}

	return nil
}

// validateLogoutResponseAttributes validates a SAML Response's tag and attributes. It does
// not inspect child elements of the Response at all.
func (sp *SAMLServiceProvider) validateLogoutResponseAttributes(response *types.LogoutResponse) error {
	if response.Destination != "" && response.Destination != sp.ServiceProviderSLOURL {
		return ErrInvalidValue{
			Key:      DestinationAttr,
			Expected: sp.ServiceProviderSLOURL,
			Actual:   response.Destination,
		}
	}

	if response.Version != "2.0" {
		return ErrInvalidValue{
			Reason:   ReasonUnsupported,
			Key:      "SAML version",
			Expected: "2.0",
			Actual:   response.Version,
		}
	}

	return nil
}

func xmlUnmarshalElement(el *etree.Element, obj interface{}) error {
	doc := etree.NewDocument()
	doc.SetRoot(el)
	data, err := doc.WriteToBytes()
	if err != nil {
		return err
	}

	err = xml.Unmarshal(data, obj)
	if err != nil {
		return err
	}
	return nil
}

func (sp *SAMLServiceProvider) getDecryptCert() (*tls.Certificate, error) {
	if sp.SPKeyStore == nil {
		return nil, fmt.Errorf("no decryption certs available")
	}

	//This is the tls.Certificate we'll use to decrypt any encrypted assertions
	var decryptCert tls.Certificate

	switch crt := sp.SPKeyStore.(type) {
	case dsig.TLSCertKeyStore:
		// Get the tls.Certificate directly if possible
		decryptCert = tls.Certificate(crt)

	default:

		//Otherwise, construct one from the results of GetKeyPair
		pk, cert, err := sp.SPKeyStore.GetKeyPair()
		if err != nil {
			return nil, fmt.Errorf("error getting keypair: %v", err)
		}

		decryptCert = tls.Certificate{
			Certificate: [][]byte{cert},
			PrivateKey:  pk,
		}
	}

	if sp.ValidateEncryptionCert {
		// Check Validity period of certificate
		if len(decryptCert.Certificate) < 1 || len(decryptCert.Certificate[0]) < 1 {
			return nil, fmt.Errorf("empty decryption cert")
		} else if cert, err := x509.ParseCertificate(decryptCert.Certificate[0]); err != nil {
			return nil, fmt.Errorf("invalid x509 decryption cert: %v", err)
		} else {
			now := sp.Clock.Now()
			if now.Before(cert.NotBefore) || now.After(cert.NotAfter) {
				return nil, fmt.Errorf("decryption cert is not valid at this time")
			}
		}
	}

	return &decryptCert, nil
}

func (sp *SAMLServiceProvider) decryptAssertions(el *etree.Element) error {
	var decryptCert *tls.Certificate

	decryptAssertion := func(ctx etreeutils.NSContext, encryptedElement *etree.Element) error {
		if encryptedElement.Parent() != el {
			return fmt.Errorf("found encrypted assertion with unexpected parent element: %s", encryptedElement.Parent().Tag)
		}

		detached, err := etreeutils.NSDetatch(ctx, encryptedElement) // make a detached copy
		if err != nil {
			return fmt.Errorf("unable to detach encrypted assertion: %v", err)
		}

		encryptedAssertion := &types.EncryptedAssertion{}
		err = xmlUnmarshalElement(detached, encryptedAssertion)
		if err != nil {
			return fmt.Errorf("unable to unmarshal encrypted assertion: %v", err)
		}

		if decryptCert == nil {
			decryptCert, err = sp.getDecryptCert()
			if err != nil {
				return fmt.Errorf("unable to get decryption certificate: %v", err)
			}
		}

		raw, derr := encryptedAssertion.DecryptBytes(decryptCert)
		if derr != nil {
			return fmt.Errorf("unable to decrypt encrypted assertion: %v", derr)
		}

		doc, _, err := parseResponse(raw, sp.MaximumDecompressedBodySize)
		if err != nil {
			return fmt.Errorf("unable to create element from decrypted assertion bytes: %v", err)
		}

		// Replace the original encrypted assertion with the decrypted one.
		if el.RemoveChild(encryptedElement) == nil {
			return fmt.Errorf("unable to remove encrypted assertion element")
		}

		el.AddChild(doc.Root())
		return nil
	}

	if err := etreeutils.NSFindIterate(el, SAMLAssertionNamespace, EncryptedAssertionTag, decryptAssertion); err != nil {
		return err
	} else {
		return nil
	}
}

func (sp *SAMLServiceProvider) validateElementSignature(el *etree.Element) (*etree.Element, error) {
	return sp.validationContext().Validate(el)
}

// deprecated
func (sp *SAMLServiceProvider) validateAssertionSignatures(el *etree.Element) error {
	signedAssertions := 0
	unsignedAssertions := 0
	validateAssertion := func(ctx etreeutils.NSContext, unverifiedAssertion *etree.Element) error {
		parent := unverifiedAssertion.Parent()
		if parent == nil {
			return fmt.Errorf("parent is nil")
		}
		if parent != el {
			return fmt.Errorf("found assertion with unexpected parent element: %s", unverifiedAssertion.Parent().Tag)
		}

		detached, err := etreeutils.NSDetatch(ctx, unverifiedAssertion) // make a detached copy
		if err != nil {
			return fmt.Errorf("unable to detach unverified assertion: %v", err)
		}

		assertion, err := sp.validationContext().Validate(detached)
		if err == dsig.ErrMissingSignature {
			unsignedAssertions++
			return nil
		} else if err != nil {
			return err
		}

		// Replace the original unverified Assertion with the verified one. Note that
		// if the Response is not signed, only signed Assertions (and not the parent Response) can be trusted.
		if el.RemoveChild(unverifiedAssertion) == nil {
			return fmt.Errorf("unable to remove unverified assertion element")
		}

		el.AddChild(assertion)
		signedAssertions++

		return nil
	}

	if err := etreeutils.NSFindIterate(el, SAMLAssertionNamespace, AssertionTag, validateAssertion); err != nil {
		return err
	} else if signedAssertions > 0 && unsignedAssertions > 0 {
		return fmt.Errorf("invalid to have both signed and unsigned assertions")
	} else if signedAssertions < 1 {
		return dsig.ErrMissingSignature
	} else {
		return nil
	}
}

// verifyAssertionSignaturesIfPresent iterates through assertions within a
// signed Response and verifies any that carry their own signatures. Assertions
// without signatures are left untouched (the Response envelope signature
// covers them). This prevents XML wrapping attacks where assertion content is
// tampered with inside a signed envelope.
func (sp *SAMLServiceProvider) verifyAssertionSignaturesIfPresent(responseEl *etree.Element) error {
	verifyAssertion := func(ctx etreeutils.NSContext, assertionEl *etree.Element) error {
		if assertionEl.Parent() != responseEl {
			return nil
		}

		detached, err := etreeutils.NSDetatch(ctx, assertionEl)
		if err != nil {
			return fmt.Errorf("unable to detach assertion for signature verification: %v", err)
		}

		verified, err := sp.validationContext().Validate(detached)
		if err == dsig.ErrMissingSignature {
			// No signature on this assertion — that's fine, the Response
			// envelope signature covers it.
			return nil
		} else if err != nil {
			return fmt.Errorf("assertion signature verification failed: %v", err)
		}

		// Replace the unverified assertion with the signature-verified version.
		if responseEl.RemoveChild(assertionEl) == nil {
			return fmt.Errorf("unable to remove unverified assertion element")
		}
		responseEl.AddChild(verified)
		return nil
	}

	return etreeutils.NSFindIterate(responseEl, SAMLAssertionNamespace, AssertionTag, verifyAssertion)
}

// ValidateEncodedResponse both decodes and validates, based on SP
// configuration, an encoded, signed response. It will also appropriately
// decrypt a response if the assertion was encrypted
func (sp *SAMLServiceProvider) ValidateEncodedResponse(encodedResponse string) (*types.Response, error) {
	raw, err := base64.StdEncoding.DecodeString(encodedResponse)
	if err != nil {
		return nil, err
	}

	// Parse the raw response
	doc, unverifiedResponse, err := parseResponse(raw, sp.MaximumDecompressedBodySize)
	if err != nil {
		return nil, err
	}

	var responseSignatureValidated bool
	// storing our final response to return back
	decodedResponse := &types.Response{}
	// user has decided to skip signature verification
	// just unmarshal the untrusted el

	if sp.SkipSignatureValidation {
		err = xmlUnmarshalElement(unverifiedResponse, decodedResponse)
		if err != nil {
			return nil, fmt.Errorf("unable to unmarshal response: %v", err)
		}

		decodedResponse.SignatureValidated = false
		err := sp.Validate(decodedResponse)
		if err != nil {
			return nil, err
		}
		return decodedResponse, nil
	}

	// first get SignedResponse, if any
	signedResponseEl, err := sp.validateElementSignature(unverifiedResponse)

	// continue for unsigned Response, maybe individual Assertions are still signed
	if err == dsig.ErrMissingSignature {
		// Unfortunately we just blew away our Response
		unverifiedResponse = doc.Root()
	} else if err != nil {
		return nil, err
	} else if signedResponseEl == nil {
		return nil, fmt.Errorf("missing transformed response")
	} else {
		// good case, no errors when verifying signature
		// 1. Response is signed
		// optionally decrypt each assertion
		err = sp.decryptAssertions(signedResponseEl)
		if err != nil {
			return nil, err
		}

		// Even though the Response envelope is signed, verify assertion
		// signatures when present. This prevents XML wrapping attacks
		// where an attacker tampers with assertion content within a
		// signed envelope.
		err = sp.verifyAssertionSignaturesIfPresent(signedResponseEl)
		if err != nil {
			return nil, err
		}

		responseSignatureValidated = true

		err = xmlUnmarshalElement(signedResponseEl, decodedResponse)
		if err != nil {
			return nil, fmt.Errorf("unable to unmarshal response: %v", err)
		}
		decodedResponse.SignatureValidated = responseSignatureValidated

		err := sp.Validate(decodedResponse)
		if err != nil {
			return nil, err
		}
		return decodedResponse, nil
	}

	// now we have a tricky case,
	// unsigned response but have some signed Assertions
	// unmarshal into decodedResponse,

	err = xmlUnmarshalElement(unverifiedResponse, decodedResponse)
	if err != nil {
		return nil, err
	}

	// keep in mind anything inside the Response is technically untrusted
	// however, we have to keep the relevant details such as StatusCode
	// We reset the underlying assertions & encrypted assertions to []

	decodedResponse.SignatureValidated = false
	decodedResponse.Assertions = []types.Assertion{}
	decodedResponse.EncryptedAssertions = []types.EncryptedAssertion{}

	// first decrypt all assertions
	err = sp.decryptAssertions(unverifiedResponse)
	if err != nil {
		return nil, err
	}

	// iterate through each Assertion inside our etree unverifiedResponse
	addSignedAssertion := func(ctx etreeutils.NSContext, unverifiedAssertion *etree.Element) error {
		parent := unverifiedAssertion.Parent()
		if parent == nil {
			return fmt.Errorf("parent is nil")
		}
		if parent != unverifiedResponse {
			return fmt.Errorf("found assertion with unexpected parent element: %s", unverifiedAssertion.Parent().Tag)
		}

		detached, err := etreeutils.NSDetatch(ctx, unverifiedAssertion) // make a detached copy
		if err != nil {
			return fmt.Errorf("unable to detach unverified assertion: %v", err)
		}

		// signedAssertion after checking for errors
		signedAssertion, err := sp.validationContext().Validate(detached)

		if err != nil {
			return err // return any errors including unsignedAssertions
		}

		decodedAssertion := &types.Assertion{}

		err = xmlUnmarshalElement(signedAssertion, decodedAssertion)
		if err != nil {
			return fmt.Errorf("unable to unmarshal assertion: %v", err)
		}

		decodedAssertion.SignatureValidated = true

		// now add it to decodedResponse
		decodedResponse.Assertions = append(decodedResponse.Assertions, *decodedAssertion)

		return nil
	}

	// iterate through each Assertion through our unverified Response
	// our decodedResponse contains a empty list of Assertions
	// throughout iteration, we will add signed assertions to the decodedResponse
	if err := etreeutils.NSFindIterate(unverifiedResponse, SAMLAssertionNamespace, AssertionTag, addSignedAssertion); err != nil {
		return nil, err
	}

	err = sp.Validate(decodedResponse)
	if err != nil {
		return nil, err
	}

	return decodedResponse, nil
}

// DecodeUnverifiedBaseResponse decodes several attributes from a SAML response for the purpose
// of determining how to validate the response. This is useful for Service Providers which
// expose a single Assertion Consumer Service URL but consume Responses from many IdPs.
func DecodeUnverifiedBaseResponse(encodedResponse string) (*types.UnverifiedBaseResponse, error) {
	raw, err := base64.StdEncoding.DecodeString(encodedResponse)
	if err != nil {
		return nil, err
	}

	var response *types.UnverifiedBaseResponse

	err = maybeDeflate(raw, defaultMaxDecompressedResponseSize, func(maybeXML []byte) error {
		response = &types.UnverifiedBaseResponse{}
		return xml.Unmarshal(maybeXML, response)
	})
	if err != nil {
		return nil, err
	}

	return response, nil
}

// maybeDeflate invokes the passed decoder over the passed data. If an error is
// returned, it then attempts to deflate the passed data before re-invoking
// the decoder over the deflated data.
func maybeDeflate(data []byte, maxSize int64, decoder func([]byte) error) error {
	err := decoder(data)
	if err == nil {
		return nil
	}

	// Default to 5MB max size
	if maxSize == 0 {
		maxSize = defaultMaxDecompressedResponseSize
	}

	lr := io.LimitReader(flate.NewReader(bytes.NewReader(data)), maxSize+1)

	deflated, err := io.ReadAll(lr)
	if err != nil {
		return err
	}

	if int64(len(deflated)) > maxSize {
		return fmt.Errorf("deflated response exceeds maximum size of %d bytes", maxSize)
	}

	return decoder(deflated)
}

// parseResponse is a helper function that was refactored out so that the XML parsing behavior can be isolated and unit tested
func parseResponse(xml []byte, maxSize int64) (*etree.Document, *etree.Element, error) {
	var doc *etree.Document
	var rawXML []byte

	err := maybeDeflate(xml, maxSize, func(xml []byte) error {
		doc = etree.NewDocument()
		rawXML = xml
		return doc.ReadFromBytes(xml)
	})
	if err != nil {
		return nil, nil, err
	}

	el := doc.Root()
	if el == nil {
		return nil, nil, fmt.Errorf("unable to parse response")
	}

	// Examine the response for attempts to exploit weaknesses in Go's encoding/xml
	err = rtvalidator.Validate(bytes.NewReader(rawXML))
	if err != nil {
		return nil, nil, err
	}

	return doc, el, nil
}

// DecodeUnverifiedLogoutResponse decodes several attributes from a SAML Logout response, without doing any verifications.
func DecodeUnverifiedLogoutResponse(encodedResponse string) (*types.LogoutResponse, error) {
	raw, err := base64.StdEncoding.DecodeString(encodedResponse)
	if err != nil {
		return nil, err
	}

	var response *types.LogoutResponse

	err = maybeDeflate(raw, defaultMaxDecompressedResponseSize, func(maybeXML []byte) error {
		response = &types.LogoutResponse{}
		return xml.Unmarshal(maybeXML, response)
	})
	if err != nil {
		return nil, err
	}

	return response, nil
}

func (sp *SAMLServiceProvider) ValidateEncodedLogoutResponsePOST(encodedResponse string) (*types.LogoutResponse, error) {
	raw, err := base64.StdEncoding.DecodeString(encodedResponse)
	if err != nil {
		return nil, err
	}

	// Parse the raw response
	doc, el, err := parseResponse(raw, sp.MaximumDecompressedBodySize)
	if err != nil {
		return nil, err
	}

	var responseSignatureValidated bool
	if !sp.SkipSignatureValidation {
		el, err = sp.validateElementSignature(el)
		if err == dsig.ErrMissingSignature {
			// Unfortunately we just blew away our Response
			el = doc.Root()
		} else if err != nil {
			return nil, err
		} else if el == nil {
			return nil, fmt.Errorf("missing transformed logout response")
		} else {
			responseSignatureValidated = true
		}
	}

	decodedResponse := &types.LogoutResponse{}
	err = xmlUnmarshalElement(el, decodedResponse)
	if err != nil {
		return nil, fmt.Errorf("unable to unmarshal logout response: %v", err)
	}
	decodedResponse.SignatureValidated = responseSignatureValidated

	err = sp.ValidateDecodedLogoutResponse(decodedResponse)
	if err != nil {
		return nil, err
	}

	return decodedResponse, nil
}


================================================
FILE: decode_response_test.go
================================================
// Copyright 2016 Russell Haering et al.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package saml2

import (
	"bytes"
	"crypto/tls"
	"crypto/x509"
	"encoding/base64"
	"encoding/pem"
	"io/ioutil"
	"testing"
	"time"

	"github.com/jonboulle/clockwork"
	rtvalidator "github.com/mattermost/xml-roundtrip-validator"
	dsig "github.com/russellhaering/goxmldsig"
	"github.com/stretchr/testify/require"
)

const (
	idpCert = `
-----BEGIN CERTIFICATE-----
MIIDODCCAiCgAwIBAgIUQH54kyyeacU69J2iwz9bzeLmMaswDQYJKoZIhvcNAQEL
BQAwHTEbMBkGA1UEAwwSY29sbGVnZS5jY2N0Y2EuZWR1MB4XDTE1MDYwNDIyMTAz
MVoXDTM1MDYwNDIyMTAzMVowHTEbMBkGA1UEAwwSY29sbGVnZS5jY2N0Y2EuZWR1
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlJhN20ng2VN/cTrWtqUI
NaUsrHCkYXbm2y1PTN4b6fJI5hbvcv+LWCuLkLi3+iPGlBpcHHfrdJcyhmBHRHQ9
Sos3RIH5Lsn1IgjWe3hxQQmVeEi5xVxnw2YZGHaeX4YnI1TEBJwhtJmyitk74LHy
bPGEqOJdApUnLz54L7I+252G/cOfEqUHMbxxtmHSc/9chF8bBxQ8OzIbJsByHnqi
awQHwtsttre7n328gVqmf1VHE27cfAYiSjuK5pCsx/1kuJMBN+kg/3Gg9oi6aR50
WX1VUF3IBcnTDeiAXRz3PgsT8FlVZou6Ik9NT/Y5IHOZVGk64SRDaG8FuGxLexXr
swIDAQABo3AwbjAdBgNVHQ4EFgQUjQwaAoY3u/iToIE3ADeNEW+Uu34wTQYDVR0R
BEYwRIISY29sbGVnZS5jY2N0Y2EuZWR1hi5odHRwczovL2NvbGxlZ2UuY2NjdGNh
LmVkdTo4NDQzL2lkcC9zaGliYm9sZXRoMA0GCSqGSIb3DQEBCwUAA4IBAQB26rdx
phN1YKad3yDhLg6Y1ZwbmAjc+l4QB1KSL+cLqhDn5iMy4VdWh8HpSKRqCwofLtlw
3qOwospj+mJaguXRMpjYODRQaKRkTrCGxJhuNrQxDXL/b6FOEIJnUYenbPevuNgR
Jc1VnREhWUUXT44KN5YUz9FEiG0BsBK8ecCPKBzTQ/hwaczhpqw6uqVMqxJaTGcn
lCUHJAhVHiA8lWJ7vaNPsJ86xBFs/F76EwyFXIKQaruvcvChU7GNNSYdNJBa6HO9
9QWdGbr5aNQ4diunnBQdrdjgbQIwyhKTfbFWa2l5vbqEKDc0dwuPa6c25l8ruqxq
CQ1CF8ZDDJ0XV6Ab
-----END CERTIFICATE-----
`

	oktaCert = `
-----BEGIN CERTIFICATE-----
MIIDPDCCAiQCCQDydJgOlszqbzANBgkqhkiG9w0BAQUFADBgMQswCQYDVQQGEwJVUzETMB
EGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNU2FuIEZyYW5jaXNjbzEQMA4GA1UEChMH
SmFua3lDbzESMBAGA1UEAxMJbG9jYWxob3N0MB4XDTE0MDMxMjE5NDYzM1oXDTI3MTExOT
E5NDYzM1owYDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcT
DVNhbiBGcmFuY2lzY28xEDAOBgNVBAoTB0phbmt5Q28xEjAQBgNVBAMTCWxvY2FsaG9zdD
CCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMGvJpRTTasRUSPqcbqCG+ZnTAur
nu0vVpIG9lzExnh11o/BGmzu7lB+yLHcEdwrKBBmpepDBPCYxpVajvuEhZdKFx/Fdy6j5m
H3rrW0Bh/zd36CoUNjbbhHyTjeM7FN2yF3u9lcyubuvOzr3B3gX66IwJlU46+wzcQVhSOl
Mk2tXR+fIKQExFrOuK9tbX3JIBUqItpI+HnAow509CnM134svw8PTFLkR6/CcMqnDfDK1m
993PyoC1Y+N4X9XkhSmEQoAlAHPI5LHrvuujM13nvtoVYvKYoj7ScgumkpWNEvX652LfXO
nKYlkB8ZybuxmFfIkzedQrbJsyOhfL03cMECAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAeH
wzqwnzGEkxjzSD47imXaTqtYyETZow7XwBc0ZaFS50qRFJUgKTAmKS1xQBP/qHpStsROT3
5DUxJAE6NY1Kbq3ZbCuhGoSlY0L7VzVT5tpu4EY8+Dq/u2EjRmmhoL7UkskvIZ2n1DdERt
d+YUMTeqYl9co43csZwDno/IKomeN5qaPc39IZjikJ+nUC6kPFKeu/3j9rgHNlRtocI6S1
FdtFz9OZMQlpr0JbUt2T3xS/YoQJn6coDmJL5GTiiKM6cOe+Ur1VwzS1JEDbSS2TWWhzq8
ojLdrotYLGd9JOsoQhElmz+tMfCFQUFLExinPAyy7YHlSiVX13QH2XTu/iQQ==
-----END CERTIFICATE-----
`

	oktaCert2 = `
-----BEGIN CERTIFICATE-----
MIIDpDCCAoygAwIBAgIGAWxzAwX1MA0GCSqGSIb3DQEBCwUAMIGSMQswCQYDVQQGEwJVUzETMBEG
A1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzENMAsGA1UECgwET2t0YTEU
MBIGA1UECwwLU1NPUHJvdmlkZXIxEzARBgNVBAMMCmRldi05MDUyNTExHDAaBgkqhkiG9w0BCQEW
DWluZm9Ab2t0YS5jb20wHhcNMTkwODA4MjA1MzMzWhcNMjkwODA4MjA1NDMzWjCBkjELMAkGA1UE
BhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28xDTALBgNV
BAoMBE9rdGExFDASBgNVBAsMC1NTT1Byb3ZpZGVyMRMwEQYDVQQDDApkZXYtOTA1MjUxMRwwGgYJ
KoZIhvcNAQkBFg1pbmZvQG9rdGEuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
m+ZZF6aEG6ehLLIV6RPA+i1z6ss3HBG2bZD3efwKCDDXYUkp59AE7JsjVHMtpJPHhzHuScuHDMlu
HmkBQTW7j9XpnaRn8SfZXkwlCUHTo+HAC9lwbQxO4d4wnwgnm6FAjm1I/gbfFAobd8BR9pDxHuXE
MQ0DtQu/W3WbDUrz/bhSxPJAoVy2koQn9G0y3unm7eRwYWHeuW6GdPWV2szTtDS0c3qtUXVF5Ugg
iQYlwQu6xkfy4l8iGJL7ETa2BmJzwCFecMIct87SqNhYQwCBH54MBaHcaSsCKyimNvMY9B7RmC+H
4+awePPA1q3R/UQ3Pfom8mx6yDdKIWqlkG3MsQIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQAiURCZ
P4oJWcf1o5nm4yG15UH01g/S6Y4OUWMi6BFJy9fCrJ0h/2BZKi68SQ0uMAbdK6anxCzq3Rr5MSzW
OWPQ1Zljn3LGPsiTFdFca/GVRen5IYQ7Dr2Mvhtm+QVscEY9TDjtETbTAHEVEjwXmB21wtdIhizv
sQS7wz0A8LV+Atpbev45RiV6COmB6T6vJuFQ7ZsDZMSHZriTYiETTJvHBGd7PtbCxYNc6LRB2JDb
wlekRhVEjR0UhnM+nn2sqqbv7tDEPs63lZSDXCnR1PhscHrEuQ04rHI3OL0gCULVQFvJrj85IAZF
1QQuGUK8ozfOyFpQWAJUW71INnF/SLWv
-----END CERTIFICATE-----
`

	badInput = `<saml2:Assertion ID="id1684056077776386493060641"IssueInstant="2019-08-12T12:00:52.718Z"Version="2.0"xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion"xmlns:xs="http://www.w3.org/2001/XMLSchema"><saml2:Issuer Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity"xmlns="">http://www.okta.com/exk133onomIuOW98z357</l><ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#"><ds:SignedInfo><ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/><ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/><ds:Reference URI="#id1684056077776386493060641"><ds:Transforms><ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/><ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"><ec:InclusiveNamespaces PrefixList="xs"xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#"/></m></s><ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/><ds:DigestValue>dC1cm0pLLjIWZC6G2Pmf0JogmqHztp9W1euXPd/TUHo=</e></e></o><ds:SignatureValue>YRSCFLIkIgjbbYLyfCIc8jsP2MUJPjn+nYWRdlVIDdXtYXXxklYqdBXQsxDwNcsOAIGS75PeVGryml3oBkUDg/MfK7z/fFPLXX7c7xgh7/DBAFlSXbwlJQxuXQ5eZcGesgG6nYRwU1hpW+yN7C2ODN9KHi5TUdiEhvy8vdlFSfxdy4Mn68nG/UZBqmHHIZdRG2/Hpcs29YyaVVZUCZ0w22b7zsPuOXHuStOSTQ6isxI2R268+ZNKERYaNMCAGX4zNlT3mHBV0NnZkbO3wmlOfKksL+Qx7L64xFc3PaervxWuPqh2FoWpTCqFdliLdvUfFDszKXJKhO0bj1U0aSrdzg==</e><s><s><s></X></X></o></e><saml2:Subject xmlns=""><saml2:NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified">steven.james.johnstone@gmail.com</l><saml2:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer"><saml2:SubjectConfirmationData InResponseTo="_40a419f5-5c1c-43d0-5834-5caf268a5f01"NotOnOrAfter="2019-08-12T12:05:52.718Z"Recipient="https://127.0.0.1/login"/></l></l><saml2:Conditions NotBefore="2019-08-12T11:55:52.718Z"NotOnOrAfter="2019-08-12T12:05:52.718Z"xmlns=""><saml2:AudienceRestriction><saml2:Audience>37a8eec1ce19687d132fe29051dca629d164e2c4958ba141d5f4133a33f0688f.jazznetworks.com</l></l></l><saml2:AuthnStatement AuthnInstant="2019-08-12T12:00:52.718Z"SessionIndex="_40a419f5-5c1c-43d0-5834-5caf268a5f01"xmlns=""><saml2:AuthnContext><saml2:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport</l></l></l><saml2:AttributeStatement xmlns=""><saml2:Attribute Name="FirstName"NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified"><saml2:AttributeValue xmlns=""xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:type="xs:string">Steven</l></l><saml2:Attribute Name="LastName"NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified"><saml2:AttributeValue xmlns=""xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:type="xs:string">Johnstone</l></l><saml2:Attribute Name="Email"NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified"><saml2:AttributeValue xmlns=""xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:type="xs:string">steven.james.johnstone@gmail.com`
)

func testEncryptedAssertion(t *testing.T, validateEncryptionCert bool) {
	var err error
	cert, err := tls.LoadX509KeyPair("./testdata/test.crt", "./testdata/test.key")
	require.NoError(t, err, "could not load x509 key pair")

	block, _ := pem.Decode([]byte(idpCert))

	idpCert, err := x509.ParseCertificate(block.Bytes)
	require.NoError(t, err, "couldn't parse idp cert pem block")

	sp := SAMLServiceProvider{
		AssertionConsumerServiceURL: "https://saml2.test.astuart.co/sso/saml2",
		SPKeyStore:                  dsig.TLSCertKeyStore(cert),
		ValidateEncryptionCert:      validateEncryptionCert,
		IDPCertificateStore: &dsig.MemoryX509CertificateStore{
			Roots: []*x509.Certificate{idpCert},
		},
		Clock: dsig.NewFakeClockAt(time.Date(2016, 04, 28, 22, 00, 00, 00, time.UTC)),
	}

	bs, err := ioutil.ReadFile("./testdata/saml.post")
	require.NoError(t, err, "couldn't read post")

	_, err = sp.RetrieveAssertionInfo(string(bs))
	if validateEncryptionCert {
		require.Error(t, err)
		require.Equal(t, "error validating response: unable to get decryption certificate: decryption cert is not valid at this time", err.Error())
	} else {
		require.NoError(t, err, "Assertion info should be retrieved with no error")
	}
}

func TestEncryptedAssertion(t *testing.T) {
	testEncryptedAssertion(t, false)
}

func TestEncryptedAssertionInvalidCert(t *testing.T) {
	testEncryptedAssertion(t, true)
}

func TestCompressedResponse(t *testing.T) {
	bs, err := ioutil.ReadFile("./testdata/saml_compressed.post")
	require.NoError(t, err, "couldn't read compressed post")

	block, _ := pem.Decode([]byte(oktaCert))

	idpCert, err := x509.ParseCertificate(block.Bytes)
	require.NoError(t, err, "couldn't parse okta cert pem block")

	sp := SAMLServiceProvider{
		AssertionConsumerServiceURL: "https://f1f51ddc.ngrok.io/api/sso/saml2/acs/58cafd0573d4f375b8e70e8e",
		SPKeyStore:                  dsig.TLSCertKeyStore(cert),
		IDPCertificateStore: &dsig.MemoryX509CertificateStore{
			Roots: []*x509.Certificate{idpCert},
		},
		Clock: dsig.NewFakeClock(clockwork.NewFakeClockAt(time.Date(2017, 3, 17, 20, 00, 0, 0, time.UTC))),
	}

	_, err = sp.RetrieveAssertionInfo(string(bs))
	require.NoError(t, err, "Assertion info should be retrieved with no error")
}

func TestDecodeColonsInLocalNames(t *testing.T) {
	// Handling of double colons was improved in Go 1.7 such that this test no longer fails.
	// See: https://go-review.googlesource.com/c/go/+/277892
	if rtvalidator.Validate(bytes.NewReader([]byte(`<x::Root/>`))) == nil {
		t.Skip()
	}

	_, _, err := parseResponse([]byte(`<x::Root/>`), 0)
	require.Error(t, err)
}

func TestDecodeDoubleColonInjectionAttackResponse(t *testing.T) {
	// Handling of double colons was improved in Go 1.7 such that this test no longer fails.
	// See: https://go-review.googlesource.com/c/go/+/277892
	if rtvalidator.Validate(bytes.NewReader([]byte(`<x::Root/>`))) == nil {
		t.Skip()
	}

	_, _, err := parseResponse([]byte(doubleColonAssertionInjectionAttackResponse), 0)
	require.Error(t, err)
}

func TestMalFormedInput(t *testing.T) {
	block, _ := pem.Decode([]byte(oktaCert2))
	idpCert, err := x509.ParseCertificate(block.Bytes)
	require.NoError(t, err, "couldn't parse okta cert pem block")

	certStore := dsig.MemoryX509CertificateStore{
		Roots: []*x509.Certificate{idpCert},
	}

	sp := &SAMLServiceProvider{
		Clock:                       dsig.NewFakeClock(clockwork.NewFakeClockAt(time.Date(2019, 8, 12, 12, 00, 52, 718, time.UTC))),
		AssertionConsumerServiceURL: "https://saml2.test.astuart.co/sso/saml2",
		SignAuthnRequests:           true,
		IDPCertificateStore:         &certStore,
		ValidateEncryptionCert:      true,
	}
	base64Input := base64.StdEncoding.EncodeToString([]byte(badInput))
	_, err = sp.RetrieveAssertionInfo(base64Input)
	require.Errorf(t, err, "parent is nil")
}

func TestCompressionBombInput(t *testing.T) {
	bs, err := ioutil.ReadFile("./testdata/saml_compressed.post")
	require.NoError(t, err, "couldn't read compressed post")

	block, _ := pem.Decode([]byte(oktaCert))

	idpCert, err := x509.ParseCertificate(block.Bytes)
	require.NoError(t, err, "couldn't parse okta cert pem block")

	sp := SAMLServiceProvider{
		AssertionConsumerServiceURL: "https://f1f51ddc.ngrok.io/api/sso/saml2/acs/58cafd0573d4f375b8e70e8e",
		SPKeyStore:                  dsig.TLSCertKeyStore(cert),
		IDPCertificateStore: &dsig.MemoryX509CertificateStore{
			Roots: []*x509.Certificate{idpCert},
		},
		Clock:                       dsig.NewFakeClock(clockwork.NewFakeClockAt(time.Date(2017, 3, 17, 20, 00, 0, 0, time.UTC))),
		MaximumDecompressedBodySize: 2048,
	}

	_, err = sp.RetrieveAssertionInfo(string(bs))
	require.Error(t, err, "error validating response: deflated response exceeds maximum size of 2048 bytes")
}


================================================
FILE: go.mod
================================================
module github.com/russellhaering/gosaml2

go 1.25.0

toolchain go1.26.1

require (
	github.com/beevik/etree v1.6.0
	github.com/jonboulle/clockwork v0.5.0
	github.com/mattermost/xml-roundtrip-validator v0.1.0
	github.com/russellhaering/goxmldsig v1.6.0
	github.com/stretchr/testify v1.11.1
)

require (
	github.com/davecgh/go-spew v1.1.1 // indirect
	github.com/kr/text v0.2.0 // indirect
	github.com/pmezard/go-difflib v1.0.0 // indirect
	gopkg.in/yaml.v3 v3.0.1 // indirect
)


================================================
FILE: go.sum
================================================
github.com/beevik/etree v1.6.0 h1:u8Kwy8pp9D9XeITj2Z0XtA5qqZEmtJtuXZRQi+j03eE=
github.com/beevik/etree v1.6.0/go.mod h1:bh4zJxiIr62SOf9pRzN7UUYaEDa9HEKafK25+sLc0Gc=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/jonboulle/clockwork v0.5.0 h1:Hyh9A8u51kptdkR+cqRpT1EebBwTn1oK9YfGYbdFz6I=
github.com/jonboulle/clockwork v0.5.0/go.mod h1:3mZlmanh0g2NDKO5TWZVJAfofYk64M7XN3SzBPjZF60=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/mattermost/xml-roundtrip-validator v0.1.0 h1:RXbVD2UAl7A7nOTR4u7E3ILa4IbtvKBHw64LDsmu9hU=
github.com/mattermost/xml-roundtrip-validator v0.1.0/go.mod h1:qccnGMcpgwcNaBnxqpJpWWUiPNr5H3O8eDgGV9gT5To=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
github.com/russellhaering/goxmldsig v1.6.0 h1:8fdWXEPh2k/NZNQBPFNoVfS3JmzS4ZprY/sAOpKQLks=
github.com/russellhaering/goxmldsig v1.6.0/go.mod h1:TrnaquDcYxWXfJrOjeMBTX4mLBeYAqaHEyUeWPxZlBM=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=


================================================
FILE: internal/fuzz/README.md
================================================
# Internal Fuzzing for gosaml2

This directory contains fuzzing targets for gosaml2 that are used with Go's built-in fuzzing functionality and OSS-Fuzz.

## Running Fuzzers Locally

```bash
go test -fuzz=FuzzDecodeResponse ./internal/fuzz/ -fuzztime=30s
go test -fuzz=FuzzLogoutResponse ./internal/fuzz/ -fuzztime=30s
go test -fuzz=FuzzBuildRequest ./internal/fuzz/ -fuzztime=30s
```

## OSS-Fuzz Integration

These fuzzers use native Go fuzzing (`func Fuzz(f *testing.F)`) and are compiled
by OSS-Fuzz using `compile_native_go_fuzzer`. Configuration files for the integration
can be found in the `oss-fuzz` directory.

================================================
FILE: internal/fuzz/fuzz_test.go
================================================
// Copyright 2025 Russell Haering et al.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package fuzz

import (
	"encoding/base64"
	"encoding/binary"
	"testing"

	saml2 "github.com/russellhaering/gosaml2"
)

func FuzzDecodeResponse(f *testing.F) {
	f.Fuzz(func(t *testing.T, data []byte) {
		encodedResponse := base64.StdEncoding.EncodeToString(data)

		_, err := saml2.DecodeUnverifiedBaseResponse(encodedResponse)
		if err != nil {
			return
		}

		sp := &saml2.SAMLServiceProvider{}
		_, _ = sp.ValidateEncodedResponse(encodedResponse)
	})
}

func FuzzLogoutResponse(f *testing.F) {
	f.Fuzz(func(t *testing.T, data []byte) {
		encodedResponse := base64.StdEncoding.EncodeToString(data)

		_, err := saml2.DecodeUnverifiedLogoutResponse(encodedResponse)
		if err != nil {
			return
		}

		sp := &saml2.SAMLServiceProvider{}
		_, _ = sp.ValidateEncodedLogoutResponsePOST(encodedResponse)
	})
}

func FuzzBuildRequest(f *testing.F) {
	f.Fuzz(func(t *testing.T, data []byte) {
		if len(data) < 8 {
			return
		}

		idValue := binary.LittleEndian.Uint64(data[:8])
		relayState := string(data[8:])

		if len(relayState) == 0 {
			return
		}

		sp := &saml2.SAMLServiceProvider{
			IdentityProviderSSOURL:      "https://idp.example.com/sso",
			IdentityProviderIssuer:      "https://idp.example.com/",
			AssertionConsumerServiceURL: "https://sp.example.com/acs",
			AudienceURI:                 "https://sp.example.com/audience",
			SignAuthnRequests:           idValue%2 == 0,
			ForceAuthn:                  idValue%3 == 0,
			IsPassive:                   idValue%5 == 0,
		}

		_, _ = sp.BuildAuthURL(relayState)
		_, _ = sp.BuildAuthRequest()
	})
}


================================================
FILE: logout_request.go
================================================
// Copyright 2016 Russell Haering et al.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package saml2

import (
	"encoding/xml"
	"github.com/russellhaering/gosaml2/types"
	"time"
)

// LogoutRequest is the go struct representation of a logout request
type LogoutRequest struct {
	XMLName xml.Name `xml:"urn:oasis:names:tc:SAML:2.0:protocol LogoutRequest"`
	ID      string   `xml:"ID,attr"`
	Version string   `xml:"Version,attr"`
	//ProtocolBinding     string          `xml:",attr"`

	IssueInstant time.Time `xml:"IssueInstant,attr"`

	Destination string        `xml:"Destination,attr"`
	Issuer      *types.Issuer `xml:"Issuer"`

	NameID             *types.NameID `xml:"NameID"`
	SignatureValidated bool          `xml:"-"` // not read, not dumped
}


================================================
FILE: oss-fuzz/Dockerfile
================================================
# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
#
################################################################################

FROM gcr.io/oss-fuzz-base/base-builder-go
RUN apt-get update && apt-get install -y make cmake
RUN git clone --depth 1 https://github.com/russellhaering/gosaml2
WORKDIR gosaml2
COPY build.sh $SRC/
COPY *.options $SRC/ 

================================================
FILE: oss-fuzz/README.md
================================================
# OSS-Fuzz Integration for gosaml2

This directory contains the configuration files necessary for integrating gosaml2 with Google's [OSS-Fuzz](https://github.com/google/oss-fuzz) continuous fuzzing service.

## Files

- `build.sh`: Build script that compiles the fuzzing targets and creates the seed corpora
- `Dockerfile`: Defines the Docker container used for building the fuzzers
- `project.yaml`: Project configuration for OSS-Fuzz
- `fuzz_decode_response.options`: Fuzzer-specific options for the SAML response decoder

## Fuzzing Targets

The actual fuzzing targets are implemented in the `internal/fuzz` directory:

1. `FuzzDecodeResponse`: Fuzzes SAML response decoding and validation
2. `FuzzLogoutResponse`: Fuzzes SAML logout response decoding
3. `FuzzBuildRequest`: Fuzzes SAML authentication request building
4. `FuzzXMLValidation`: Fuzzes XML validation to catch parsing vulnerabilities

## Testing Locally with Docker

To test the OSS-Fuzz integration locally:

```bash
# Clone OSS-Fuzz
git clone https://github.com/google/oss-fuzz
cd oss-fuzz

# Build the image
python infra/helper.py build_image gosaml2

# Build the fuzzers
python infra/helper.py build_fuzzers gosaml2

# Run the fuzzers
python infra/helper.py run_fuzzer gosaml2 fuzz_decode_response
```

## Adding New Fuzzers

To add a new fuzzer:

1. Add the fuzzer implementation to `internal/fuzz/`
2. Update `build.sh` to compile the new fuzzer and create its seed corpus
3. Create fuzzer options file if needed (e.g., `my_new_fuzzer.options`) 

================================================
FILE: oss-fuzz/build.sh
================================================
#!/bin/bash -eu
# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
################################################################################

cd $SRC/gosaml2

# Build fuzzers
compile_native_go_fuzzer github.com/russellhaering/gosaml2/internal/fuzz FuzzDecodeResponse fuzz_decode_response
compile_native_go_fuzzer github.com/russellhaering/gosaml2/internal/fuzz FuzzLogoutResponse fuzz_logout_response
compile_native_go_fuzzer github.com/russellhaering/gosaml2/internal/fuzz FuzzBuildRequest fuzz_build_request

# Create seed corpus
mkdir -p $OUT/fuzz_decode_response_seed_corpus
# Use existing test data as seed corpus
find ./testdata -name '*.b64' -o -name '*.xml' | while read f; do
  cp "$f" $OUT/fuzz_decode_response_seed_corpus/
done
zip -j $OUT/fuzz_decode_response_seed_corpus.zip $OUT/fuzz_decode_response_seed_corpus/*
rm -rf $OUT/fuzz_decode_response_seed_corpus

# Create a minimal seed corpus for the logout response
mkdir -p $OUT/fuzz_logout_response_seed_corpus
# Find logout response files if they exist, otherwise use a subset of the general ones
find ./testdata -name '*logout*' -o -name '*.b64' | head -n 5 | while read f; do
  cp "$f" $OUT/fuzz_logout_response_seed_corpus/
done
zip -j $OUT/fuzz_logout_response_seed_corpus.zip $OUT/fuzz_logout_response_seed_corpus/*
rm -rf $OUT/fuzz_logout_response_seed_corpus

# Create a minimal seed corpus for build request
mkdir -p $OUT/fuzz_build_request_seed_corpus
echo "relayState" > $OUT/fuzz_build_request_seed_corpus/relaystate
echo "state123456" > $OUT/fuzz_build_request_seed_corpus/state
zip -j $OUT/fuzz_build_request_seed_corpus.zip $OUT/fuzz_build_request_seed_corpus/*
rm -rf $OUT/fuzz_build_request_seed_corpus


================================================
FILE: oss-fuzz/fuzz_decode_response.options
================================================
[libfuzzer]
max_len = 10240


================================================
FILE: oss-fuzz/project.yaml
================================================
homepage: "https://github.com/russellhaering/gosaml2"
primary_contact: "russell.haering@gmail.com"
language: go
main_repo: "https://github.com/russellhaering/gosaml2"

fuzzing_engines:
  - libfuzzer
sanitizers:
  - address

architectures:
  - x86_64

================================================
FILE: providertests/exercise.go
================================================
// Copyright 2016 Russell Haering et al.
// 
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// 
//     https://www.apache.org/licenses/LICENSE-2.0
// 
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// +build go1.7

package providertests

import (
	"testing"

	saml2 "github.com/russellhaering/gosaml2"
	"github.com/stretchr/testify/require"
)

func ExerciseProviderTestScenarios(t *testing.T, scenarios []ProviderTestScenario) {
	for _, scenario := range scenarios {
		t.Run(scenario.ScenarioName, func(t *testing.T) {
			_, err := saml2.DecodeUnverifiedBaseResponse(scenario.Response)
			// DecodeUnverifiedBaseResponse is more permissive than RetrieveAssertionInfo.
			// If an error _is_ returned it should match, but it is OK for no error to be
			// returned even when one is expected during full validation.
			if err != nil {
				scenario.CheckError(t, err)
			}

			assertionInfo, err := scenario.ServiceProvider.RetrieveAssertionInfo(scenario.Response)
			if scenario.CheckError != nil {
				scenario.CheckError(t, err)
			} else {
				require.NoError(t, err)
			}

			if err == nil {
				if scenario.CheckWarningInfo != nil {
					scenario.CheckWarningInfo(t, assertionInfo.WarningInfo)
				} else {
					require.False(t, assertionInfo.WarningInfo.InvalidTime)
					require.False(t, assertionInfo.WarningInfo.NotInAudience)
				}
			}
		})
	}
}


================================================
FILE: providertests/exercise_go_1_6.go
================================================
// Copyright 2016 Russell Haering et al.
// 
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// 
//     https://www.apache.org/licenses/LICENSE-2.0
// 
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// +build !go1.7

package providertests

import (
	"testing"

	saml2 "github.com/russellhaering/gosaml2"
	"github.com/stretchr/testify/require"
)

func ExerciseProviderTestScenarios(t *testing.T, scenarios []ProviderTestScenario) {
	for _, scenario := range scenarios {
		// DecodeUnverifiedBaseResponse is more permissive than RetrieveAssertionInfo.
		// If an error _is_ returned it should match, but it is OK for no error to be
		// returned even when one is expected during full validation.
		_, err := saml2.DecodeUnverifiedBaseResponse(scenario.Response)
		if err != nil {
			scenario.CheckError(t, err)
		}

		assertionInfo, err := scenario.ServiceProvider.RetrieveAssertionInfo(scenario.Response)
		if scenario.CheckError != nil {
			scenario.CheckError(t, err)
		} else {
			require.NoError(t, err)
		}

		if err == nil {
			if scenario.CheckWarningInfo != nil {
				scenario.CheckWarningInfo(t, assertionInfo.WarningInfo)
			} else {
				require.False(t, assertionInfo.WarningInfo.InvalidTime)
				require.False(t, assertionInfo.WarningInfo.NotInAudience)
			}
		}
	}
}


================================================
FILE: providertests/oktadev_test.go
================================================
// Copyright 2016 Russell Haering et al.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package providertests

import (
	"fmt"
	"testing"
	"time"

	"github.com/jonboulle/clockwork"
	saml2 "github.com/russellhaering/gosaml2"
	dsig "github.com/russellhaering/goxmldsig"
)

var oktaScenarioErrors = map[int]string{
	1:  "error validating response: Missing signature referencing the top-level element",
	3:  "error validating response: Could not verify certificate against trusted certs",
	4:  "error validating response: Unrecognized Destination value, Expected: http://dba9a5fc.ngrok.io/v1/_saml_callback, Actual: fake.identifier.example.com",
	5:  "error validating response: Unrecognized Issuer value, Expected: http://example.com/saml/acs/example, Actual: fake.identifier.example.com",
	7:  "error validating response: missing Issuer element",
	8:  "error validating response: missing NotOnOrAfter attribute on SubjectConfirmationData element",
	9:  "missing NotOnOrAfter attribute on Conditions element",
	10: "missing NotBefore attribute on Conditions element",
	12: "error validating response: Missing signature referencing the top-level element",
	13: "error validating response: Signature could not be verified",
	14: "error validating response: Unrecognized StatusCode value, Expected: urn:oasis:names:tc:SAML:2.0:status:Success, Actual: Failure",
	15: "error validating response: Unrecognized StatusCode value, Expected: urn:oasis:names:tc:SAML:2.0:status:Success, Actual: urn:oasis:names:tc:SAML:2.0:status:Requester",
}

var oktaScenarioWarnings = map[int]scenarioWarnings{
	6: scenarioWarnings{
		NotInAudience: true,
	},
	11: scenarioWarnings{
		InvalidTime: true,
	},
}

func TestOktaDevCasesLocally(t *testing.T) {
	sp := &saml2.SAMLServiceProvider{
		IdentityProviderSSOURL:      "http://example.com/saml/acs/example",
		IdentityProviderIssuer:      "http://example.com/saml/acs/example",
		AssertionConsumerServiceURL: "http://dba9a5fc.ngrok.io/v1/_saml_callback",
		AudienceURI:                 "http://example.com/saml/acs/example",
		IDPCertificateStore:         LoadCertificateStore("./testdata/saml.oktadev.com/oktadev.pem"),
		Clock:                       dsig.NewFakeClock(clockwork.NewFakeClockAt(time.Date(2017, 4, 4, 17, 54, 0, 0, time.UTC))),
	}

	scenarios := []ProviderTestScenario{}

	for i := 0; i < 17; i++ {
		response := LoadRawResponse(fmt.Sprintf("./testdata/saml.oktadev.com/response_%d", i))
		scenarios = append(scenarios, ProviderTestScenario{
			ScenarioName:    fmt.Sprintf("Scenario_%d", i),
			Response:        response,
			ServiceProvider: sp,
			// Capture the value of i by passing it to a function.
			CheckError:       scenarioErrorChecker(i, oktaScenarioErrors),
			CheckWarningInfo: scenarioWarningChecker(i, oktaScenarioWarnings),
		})
	}

	ExerciseProviderTestScenarios(t, scenarios)
}


================================================
FILE: providertests/onelogin_test.go
================================================
// Copyright 2016 Russell Haering et al.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package providertests

import (
	"fmt"
	"testing"

	saml2 "github.com/russellhaering/gosaml2"
)

var oneLoginScenarioErrors = map[int]string{
	// 99 - Response(Assertion) - no signature
	99: "error validating response: Missing signature referencing the top-level element",
	// 98 - Response(encrypted(Assertion)) - no signature
	98: "error validating response: Missing signature referencing the top-level element",
	// 01 - signed(Response(Assertion))
	1: "",
	// 03 - Response(signed(Assertion))
	3: "",
	// 04 - signed(Response(signed(Assertion)))
	4: "",
	// 05 - Response(encrypted(signed(Assertion))), no encryption certificate included in Assertion.
	// OneLogin does not include SP encryption certificate by default.  ADFS and PingFed also do not
	// include the SP encryption certificate and they do not provide an option to include it in Response.
	// OneLogin and PingFed also do not include DigestMethod (default to http://www.w3.org/2000/09/xmldsig#sha1).
	5: "",
	// 06 - Response(encrypted(signed(Assertion)))
	6: "",
	// 07 - signed(Response(encrypted(Assertion)))
	7: "",
	// 08 - signed(Response(encrypted(signed(Assertion))))
	8: "",
	// 09 - signed(Response(encrypted(signed(Assertion)))), no encryption certificate included in Assertion.
	// OneLogin does not include SP encryption certificate by default.  ADFS and PingFed also do not
	// include the SP encryption certificate and they do not provide an option to include it in Response.
	// OneLogin and PingFed also do not include DigestMethod (default to http://www.w3.org/2000/09/xmldsig#sha1).
	9: "",
	// 11 - signed(Response(Assertion)) - 01 Assertion content changed slightly
	11: "error validating response: Signature could not be verified",
	// 12 - signed(Response(Assertion)) - 01 Response content changed slightly
	12: "error validating response: Signature could not be verified",
	// 13 - Response(signed(Assertion)) - 03 Assertion content changed slightly
	13: "error validating response: Signature could not be verified",
	// 14 - signed(Response(signed(Assertion)) - 04 Assertion content changed slightly
	14: "error validating response: Signature could not be verified",
	// 15 - signed(Response(signed(Assertion))) - 04 Response content changed slightly
	15: "error validating response: Signature could not be verified",
	// 16 - Response(encrypted(signed(Assertion))) - 06 CipherValue of EncryptedKey changed slightly
	16: "error validating response: unable to decrypt encrypted assertion: cannot decrypt, error retrieving private key: rsa internal error: crypto/rsa: decryption error",
	// 17 - signed(Response(encrypted(Assertion))) - 07 Response content changed slightly
	17: "error validating response: Signature could not be verified",
	// 18 - signed(Response(encrypted(signed(Assertion)))) - 16 signed (signature valid, still cannot decrypt)
	18: "error validating response: unable to decrypt encrypted assertion: cannot decrypt, error retrieving private key: rsa internal error: crypto/rsa: decryption error",
	// 81 - Response(Assertion) - 99 missing assertion and response signature
	81: "error validating response: Missing signature referencing the top-level element",
	// 82 - Response(Assertion) - 99 missing assertion and response signature
	82: "error validating response: Missing signature referencing the top-level element",
	// 91 - Response(Assertion) - 99 missing Response subject confirmation element
	// Note: gosaml2 is correctly checking signature before contents
	91: "error validating response: Missing signature referencing the top-level element",
	// 92 - Response(Assertion) - 99 missing Response subject confirmation method
	// Note: gosaml2 is correctly checking signature before contents
	92: "error validating response: Missing signature referencing the top-level element",
	// 21 - signed(Response(Assertion)) - 91 sign Response, IssueInstant before SigningCertTime
	21: "error validating response: Cert is not valid at this time",
	// 22 - signed(Response(Assertion)) - 92 sign Response, IssueInstant after SigningCertTime
	22: "error validating response: Cert is not valid at this time",
	// 93 - Response(signed(Assertion)) - 91 sign Assertion, IssueInstant before SigningCertTime
	93: "error validating response: Cert is not valid at this time",
	// 94 - Response(signed(Assertion)) - 92 sign Assertion, IssueInstant after SigningCertTime
	94: "error validating response: Cert is not valid at this time",
	// 83 - Response(signed(Assertion)) - 81 sign Assertion IssueInstant before EncryptionCertTime
	//                                    (Success, EncryptionCertTime is not a factor in this case)
	83: "",
	// 84 - Response(signed(Assertion)) - 82 sign Assertion, IssueInstant after EncryptionCertTime
	//	                                  (Success, EncryptionCertTime is not a factor in this case)
	84: "",
	// 23 - Response(encrypted(signed(Assertion))) - 83 encrypt Assertion, IssueInstant before EncryptionCertTime
	23: "error validating response: unable to get decryption certificate: decryption cert is not valid at this time",
	// 24 - Response(encrypted(signed(Assertion))) - 84 encrypt Assertion, IssueInstant after EncryptionCertTime
	24: "error validating response: unable to get decryption certificate: decryption cert is not valid at this time",
	// 26 - signed(Response(Assertion)) - 01 with AtTime after IssueInstant
	26: "error validating response: Expired NotOnOrAfter value, Expected: 2017-08-30T23:55:00Z, Actual: 2017-08-30T23:19:41.379Z",
	// 28 - Response(encrypted(signed(Assertion))) - 06 with AtTime after IssueInstant
	28: "error validating response: Expired NotOnOrAfter value, Expected: 2017-08-30T23:55:00Z, Actual: 2017-08-30T23:19:41.379Z",
	// 31 - signed(Response(Assertion)) - 01 wrong IDP signing cert
	31: "error validating response: Could not verify certificate against trusted certs",
	// 33 - Response(signed(Assertion)) - 03 wrong IDP signing cert
	33: "error validating response: Could not verify certificate against trusted certs",
	// 34 - signed(Response(signed(Assertion))) - 04 wrong IDP signing cert
	34: "error validating response: Could not verify certificate against trusted certs",
	// 36 - Response(encrypted(signed(Assertion))) - 06 wrong IDP signing cert, correct SP encryption cert
	36: "error validating response: Could not verify certificate against trusted certs",
	// 37 - signed(Response(encrypted(Assertion))) - 07 wrong IDP signing cert, correct SP encryption cert
	37: "error validating response: Could not verify certificate against trusted certs",
	// 38 - signed(Response(encrypted(signed(Assertion)))) - 08 wrong IDP signing cert, correct SP encryption cert
	38: "error validating response: Could not verify certificate against trusted certs",
	// 97 - Response(encrypted(Assertion)) - 99 wrong SP encryption cert
	97: "error validating response: unable to decrypt encrypted assertion: cannot decrypt, error retrieving private key: key decryption attempted with mismatched cert, SP cert(cd:f6:7c:e9), assertion cert(42:99:58:b8)",
	// 46 - Response(encrypted(signed(Assertion))) - 06 wrong SP encryption cert, correct IDP signing cert
	46: "error validating response: unable to decrypt encrypted assertion: cannot decrypt, error retrieving private key: key decryption attempted with mismatched cert, SP cert(cd:f6:7c:e9), assertion cert(42:99:58:b8)",
	// 47 - signed(Response(encrypted(Assertion))) - 07 wrong SP encryption cert, correct IDP signing cert
	47: "error validating response: unable to decrypt encrypted assertion: cannot decrypt, error retrieving private key: key decryption attempted with mismatched cert, SP cert(cd:f6:7c:e9), assertion cert(42:99:58:b8)",
	// 48 - signed(Response(encrypted(signed(Assertion)))) - 08 wrong SP encryption cert, correct IDP signing cert
	48: "error validating response: unable to decrypt encrypted assertion: cannot decrypt, error retrieving private key: key decryption attempted with mismatched cert, SP cert(cd:f6:7c:e9), assertion cert(42:99:58:b8)",
	// 85 - Response(Assertion) - 99 empty Response Destination (empty is ok, Destination is optional)
	// Note: gosaml2 is correctly checking signature before contents
	85: "error validating response: Missing signature referencing the top-level element",
	// 86 - Response(Assertion) - 99 wrong Response Destination (SP acs)
	// Note: gosaml2 is correctly checking signature before contents
	86: "error validating response: Missing signature referencing the top-level element",
	// 87 - Response(Assertion) - 99 wrong Response Issuer (IDP endpoint id)
	// Note: gosaml2 is correctly checking signature before contents
	87: "error validating response: Missing signature referencing the top-level element",
	// 88 - Response(Assertion) - 99 wrong Assertion Audience (SP entity id)
	// Note: gosaml2 is correctly checking signature before contents
	88: "error validating response: Missing signature referencing the top-level element",
	// 89 - Response(Assertion) - 99 wrong Assertion Issuer (IDP endpoint id)
	// Note: gosaml2 is correctly checking signature before contents
	89: "error validating response: Missing signature referencing the top-level element",
	// 50 - signed(Response(Assertion)) - 85 signed Response, empty Response Destination (success, optional)
	50: "",
	// 51 - signed(Response(Assertion)) - 86 signed Response, wrong Response Destination (SP acs)
	51: "error validating response: Unrecognized Destination value, Expected: https://saml.sp.nope/session/sso/saml/acs/rq5jwkvb8z, Actual: https://saml.sp.nope/session/sso/saml/acs/incorrect",
	// 52 - signed(Response(Assertion)) - 87 signed Response, wrong Response Issuer (IDP endpoint id)
	52: "error validating response: Unrecognized Issuer value, Expected: https://saml.idp.nope/h9gkjzvb3e, Actual: https://saml.idp.nope/incorrect",
	// 54 - signed(Response(Assertion)) - 89 signed Response, wrong Assertion Issuer (IDP endpoint id)
	54: "error validating response: Unrecognized Issuer value, Expected: https://saml.idp.nope/h9gkjzvb3e, Actual: https://saml.idp.nope/incorrect",
	// 55 - Response(signed(Assertion)) - 85 signed Assertion, empty Response Destination (success, optional)
	55: "",
	// 56 - Response(signed(Assertion)) - 86 signed Assertion, wrong Response Destination (SP acs)
	56: "error validating response: Unrecognized Destination value, Expected: https://saml.sp.nope/session/sso/saml/acs/rq5jwkvb8z, Actual: https://saml.sp.nope/session/sso/saml/acs/incorrect",
	// 57 - error validating response: Response(signed(Assertion)) - 87 signed Assertion, wrong Response Issuer (IDP endpoint id)
	57: "error validating response: Unrecognized Issuer value, Expected: https://saml.idp.nope/h9gkjzvb3e, Actual: https://saml.idp.nope/incorrect",
	// 59 - Response(signed(Assertion)) - 89 signed Assertion, wrong Assertion Issuer (IDP endpoint id)
	59: "error validating response: Unrecognized Issuer value, Expected: https://saml.idp.nope/h9gkjzvb3e, Actual: https://saml.idp.nope/incorrect",
	// 155 - Response(encrypted(signed(Assertion))) - 85 encrypted signed Assertion, empty Response Destination (success, optional)
	155: "",
	// 156 - Response(encrypted(signed(Assertion))) - 86 encrypted signed Assertion, wrong Response Destination (SP acs)
	156: "error validating response: Unrecognized Destination value, Expected: https://saml.sp.nope/session/sso/saml/acs/rq5jwkvb8z, Actual: https://saml.sp.nope/session/sso/saml/acs/incorrect",
	// 157 - Response(encrypted(signed(Assertion))) - 87 encrypted signed Assertion, wrong Response Issuer (IDP endpoint id)
	157: "error validating response: Unrecognized Issuer value, Expected: https://saml.idp.nope/h9gkjzvb3e, Actual: https://saml.idp.nope/incorrect",
	// 159 - Response(encrypted(signed(Assertion))) - 89 encrypted signed Assertion, wrong Assertion Issuer (IDP endpoint id)
	159: "error validating response: Unrecognized Issuer value, Expected: https://saml.idp.nope/h9gkjzvb3e, Actual: https://saml.idp.nope/incorrect",
}

var oneLoginScenarioWarnings = map[int]scenarioWarnings{
	// 25 - signed(Response(Assertion)) - 01 with AtTime before IssueInstant
	25: scenarioWarnings{
		InvalidTime: true,
	},
	// 27 - Response(encrypted(signed(Assertion))) - 06 with AtTime before IssueInstant
	27: scenarioWarnings{
		InvalidTime: true,
	},
	// 53 - signed(Response(Assertion)) - 88 signed Response, wrong Assertion Audience (SP entity id)
	53: scenarioWarnings{
		NotInAudience: true,
	},
	// 58 - Response(signed(Assertion)) - 88 signed Assertion, wrong Assertion Audience (SP entity id)
	58: scenarioWarnings{
		NotInAudience: true,
	},
	// 158 - Response(encrypted(signed(Assertion))) - 88 encrypted signed Assertion, wrong Assertion Audience (SP entity id)
	158: scenarioWarnings{
		NotInAudience: true,
	},
}

var oneLoginAtTimes = map[int]string{
	25: "2017-08-30T23:00:00Z",
	26: "2017-08-30T23:55:00Z",
	27: "2017-08-30T23:00:00Z",
	28: "2017-08-30T23:55:00Z",
}

func TestOneLoginCasesLocally(t *testing.T) {
	sp := &saml2.SAMLServiceProvider{
		IdentityProviderSSOURL:      "https://saml.idp.nope/h9gkjzvb3e", // not required for these tests
		IdentityProviderIssuer:      "https://saml.idp.nope/h9gkjzvb3e",
		AssertionConsumerServiceURL: "https://saml.sp.nope/session/sso/saml/acs/rq5jwkvb8z",
		AudienceURI:                 "https://saml.sp.nope/session/sso/saml/spentityid/rq5jwkvb8z",
		IDPCertificateStore:         LoadCertificateStore("./testdata/onelogin/idp.signing.cert"),
		SPKeyStore:                  LoadKeyStore("./testdata/onelogin/sp.encryption.cert", "./testdata/onelogin/sp.encryption.key"),
		SPSigningKeyStore:           LoadKeyStore("./testdata/onelogin/sp.signing.cert", "./testdata/onelogin/sp.signing.key"),
		ValidateEncryptionCert:      true,
	}

	scenarios := []ProviderTestScenario{}
	for _, idx := range scenarioIndexes(oneLoginScenarioErrors, oneLoginScenarioWarnings) {
		response := LoadRawResponse(fmt.Sprintf("./testdata/onelogin/olgn09_response_%02d.b64", idx))
		scenarios = append(scenarios, ProviderTestScenario{
			ScenarioName:     fmt.Sprintf("Scenario_%02d", idx),
			Response:         response,
			ServiceProvider:  spAtTime(sp, getAtTime(idx, oneLoginAtTimes), response),
			CheckError:       scenarioErrorChecker(idx, oneLoginScenarioErrors),
			CheckWarningInfo: scenarioWarningChecker(idx, oneLoginScenarioWarnings),
		})
	}

	ExerciseProviderTestScenarios(t, scenarios)
}


================================================
FILE: providertests/pingfed_test.go
================================================
// Copyright 2016 Russell Haering et al.
// 
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// 
//     https://www.apache.org/licenses/LICENSE-2.0
// 
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package providertests

import (
	"fmt"
	"testing"

	"github.com/russellhaering/gosaml2"
)

var pingFedScenarioErrors = map[int]string{
	// 01 - signed(Response(Assertion)) - no KeyInfo (no IDP signing certificate included)
	// By default, PingFed does not include the KeyInfo element (which would include the
	// IDP signing certificate) in Response.  Most other IDPs include KeyInfo element.
	// gosaml2 is ok with this as long as the SP has only one IDP signing certificate
	// for this relationship (it does not have to guess at to which cert to use).
	// Note: Response.Destination is set.
	01: "",
	// 02 - signed(Response(Assertion)) - with KeyInfo (including IDP signing certificate)
	// Same as pfed11_response_01 except KeyInfo is included.
	// Note: Response.Destination is set.
	02: "",
	// 03 - Response(signed(Assertion))
	// As with 01, KeyInfo element is not included (not a bug).  Unlike 01,
	// Response.Destination is NOT set.  Most IDPs (including PingOne) always includes Response.Destination.
	// PingFed only includes Response.Destination when the Response is signed.
	// SAML Core 2.0 defines Response.Destination as [Optional].
	// Thus, PingFed is not in not including Destination.  Before a fix, gosaml2 required
	// Response.Destination.  gosaml2 now only checks the value of Response.Destination if it
	// is set (mandated by SAML Core 2.0).
	03: "",
	// 05 - signed(Response(encrypted(Assertion))) - no encryption certificate included in Assertion.
	// PingFed and ADFS do not include the SP encryption certificate and do not provide an option to include it in Response.
	// OneLogin (see olgn09/olgn09_response_05.b64) also does not include SP encryption certificate by default.
	// OneLogin and PingFed also do not include DigestMethod (default to http://www.w3.org/2000/09/xmldsig#sha1).
	05: "",
}

var pingFedScenarioWarnings = map[int]scenarioWarnings{}

var pingFedAtTimes = map[int]string{}

func TestPingFedCasesLocally(t *testing.T) {
	sp := &saml2.SAMLServiceProvider{
		IdentityProviderSSOURL:      "https://saml.test.nope:9031/eid/sxpmrhbkzn", // not required for these tests
		IdentityProviderIssuer:      "https://saml.test.nope:9031/eid/sxpmrhbkzn",
		AssertionConsumerServiceURL: "https://saml.test.nope/session/sso/saml/acs/hp24dqnpvq",
		AudienceURI:                 "https://saml.test.nope/session/sso/saml/spentityid/hp24dqnpvq",
		IDPCertificateStore:         LoadCertificateStore("./testdata/pingfed/idp.signing.cert"),
		SPKeyStore:                  LoadKeyStore("./testdata/pingfed/sp.encryption.cert", "./testdata/pingfed/sp.encryption.key"),
		SPSigningKeyStore:           LoadKeyStore("./testdata/pingfed/sp.signing.cert", "./testdata/pingfed/sp.signing.key"),
		ValidateEncryptionCert:      true,
	}

	scenarios := []ProviderTestScenario{}
	for _, idx := range scenarioIndexes(pingFedScenarioErrors, pingFedScenarioWarnings) {
		response := LoadRawResponse(fmt.Sprintf("./testdata/pingfed/pfed11_response_%02d.b64", idx))
		scenarios = append(scenarios, ProviderTestScenario{
			ScenarioName:     fmt.Sprintf("Scenario_%02d", idx),
			Response:         response,
			ServiceProvider:  spAtTime(sp, getAtTime(idx, pingFedAtTimes), response),
			CheckError:       scenarioErrorChecker(idx, pingFedScenarioErrors),
			CheckWarningInfo: scenarioWarningChecker(idx, pingFedScenarioWarnings),
		})
	}

	ExerciseProviderTestScenarios(t, scenarios)
}


================================================
FILE: providertests/providers_test.go
================================================
// Copyright 2016 Russell Haering et al.
// 
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// 
//     https://www.apache.org/licenses/LICENSE-2.0
// 
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package providertests

import (
	"testing"
	"time"

	"github.com/jonboulle/clockwork"
	"github.com/russellhaering/gosaml2"
	"github.com/russellhaering/goxmldsig"
)

func TestValidateResponses(t *testing.T) {
	scenarios := []ProviderTestScenario{
		{
			ScenarioName: "Auth0",
			Response:     LoadXMLResponse("./testdata/auth0_response.xml"),
			ServiceProvider: &saml2.SAMLServiceProvider{
				IdentityProviderSSOURL:      "https://scaleft-test.auth0.com/samlp/rlXOZ4kOUTQaTV8icSXrfZUd1qtD1NhK",
				IdentityProviderIssuer:      "urn:scaleft-test.auth0.com",
				AssertionConsumerServiceURL: "http://localhost:8080/v1/_saml_callback",
				AudienceURI:                 "urn:scaleft-test.auth0.com",
				IDPCertificateStore:         LoadCertificateStore("./testdata/auth0_cert.pem"),
				Clock:                       dsig.NewFakeClock(clockwork.NewFakeClockAt(time.Date(2016, 7, 25, 18, 30, 0, 0, time.UTC))),
			},
		},
		{
			ScenarioName: "Adfs",
			Response:     LoadRawResponse("./testdata/adfs_response.b64"),
			ServiceProvider: &saml2.SAMLServiceProvider{
				IdentityProviderSSOURL:      "https://do.not.need/this/not/sending/authn",
				IdentityProviderIssuer:      "http://fs.spstest2.com/adfs/services/trust",
				AssertionConsumerServiceURL: "https://saml.test.nope/session/sso/saml/acs/dknhyszjl7",
				SignAuthnRequests:           false,
				AudienceURI:                 "https://saml.test.nope/session/sso/saml/spentityid/dknhyszjl7",
				IDPCertificateStore:         LoadCertificateStore("./testdata/adfs_idp_signing_cert.pem"),
				SPKeyStore:                  LoadKeyStore("./testdata/adfs_sp_encryption_cert.pem", "./testdata/adfs_sp_encryption_key.pem"),
				SPSigningKeyStore:           LoadKeyStore("./testdata/adfs_sp_signing_cert.pem", "./testdata/adfs_sp_signing_key.pem"),
				Clock:                       dsig.NewFakeClock(clockwork.NewFakeClockAt(time.Date(2017, 9, 21, 23, 28, 0, 0, time.UTC))),
			},
		},
		{
			ScenarioName: "AdfsEncrypted",
			Response:     LoadRawResponse("./testdata/adfs_response_enc.b64"),
			ServiceProvider: &saml2.SAMLServiceProvider{
				IdentityProviderSSOURL:      "https://do.not.need/this/not/sending/authn",
				IdentityProviderIssuer:      "http://fs.spstest2.com/adfs/services/trust",
				AssertionConsumerServiceURL: "https://saml.test.nope/session/sso/saml/acs/dknhyszjl7",
				SignAuthnRequests:           false,
				AudienceURI:                 "https://saml.test.nope/session/sso/saml/spentityid/dknhyszjl7",
				IDPCertificateStore:         LoadCertificateStore("./testdata/adfs_idp_signing_cert.pem"),
				SPKeyStore:                  LoadKeyStore("./testdata/adfs_sp_encryption_cert.pem", "./testdata/adfs_sp_encryption_key.pem"),
				SPSigningKeyStore:           LoadKeyStore("./testdata/adfs_sp_signing_cert.pem", "./testdata/adfs_sp_signing_key.pem"),
				Clock:                       dsig.NewFakeClock(clockwork.NewFakeClockAt(time.Date(2017, 9, 21, 23, 20, 0, 0, time.UTC))),
			},
		},
		{
			// Okta uses detached EncryptedKey element (sibling of EncryptedData).  See:
			// https://www.w3.org/TR/2002/REC-xmlenc-core-20021210/Overview.html#sec-Extensions-to-KeyInfo
			ScenarioName: "OktaEncrypted",
			Response:     LoadRawResponse("./testdata/oktaenc_response.b64"),
			ServiceProvider: &saml2.SAMLServiceProvider{
				IdentityProviderSSOURL:      "https://do.not.need/this/not/sending/authn",
				IdentityProviderIssuer:      "http://www.okta.com/exkbb59wb20X96NY20h7",
				AssertionConsumerServiceURL: "https://saml.test.nope/session/sso/saml/acs/skv8dv26wh",
				SignAuthnRequests:           false,
				AudienceURI:                 "https://saml.test.nope/session/sso/saml/spentityid/skv8dv26wh",
				IDPCertificateStore:         LoadCertificateStore("./testdata/oktaenc_idp_signing_cert.pem"),
				SPKeyStore:                  LoadKeyStore("./testdata/oktaenc_sp_encryption_cert.pem", "./testdata/oktaenc_sp_encryption_key.pem"),
				SPSigningKeyStore:           LoadKeyStore("./testdata/oktaenc_sp_signing_cert.pem", "./testdata/oktaenc_sp_signing_key.pem"),
				Clock:                       dsig.NewFakeClock(clockwork.NewFakeClockAt(time.Date(2017, 9, 06, 22, 14, 0, 0, time.UTC))),
			},
		},
		{
			ScenarioName: "Okta",
			Response:     LoadXMLResponse("./testdata/okta_response.xml"),
			ServiceProvider: &saml2.SAMLServiceProvider{
				IdentityProviderSSOURL:      "https://dev-116807.oktapreview.com/app/scaleftdev116807_test_1/exk659aytfMeNI49v0h7/sso/saml",
				IdentityProviderIssuer:      "http://www.okta.com/exk659aytfMeNI49v0h7",
				AssertionConsumerServiceURL: "http://localhost:8080/v1/_saml_callback",
				SignAuthnRequests:           true,
				AudienceURI:                 "\"123\"",
				IDPCertificateStore:         LoadCertificateStore("./testdata/okta_cert.pem"),
				Clock:                       dsig.NewFakeClock(clockwork.NewFakeClockAt(time.Date(2016, 7, 25, 23, 16, 0, 0, time.UTC))),
			},
		},
		{
			ScenarioName: "OneLogin",
			Response:     LoadXMLResponse("./testdata/onelogin_response.xml"),
			ServiceProvider: &saml2.SAMLServiceProvider{
				IdentityProviderSSOURL:      "https://launchdarkly-dev.onelogin.com/trust/saml2/http-post/sso/634027",
				IdentityProviderIssuer:      "https://app.onelogin.com/saml/metadata/634027",
				AssertionConsumerServiceURL: "http://884d40bf.ngrok.io/api/sso/saml2/acs/58af624473d4f375b8e70d81",
				IDPCertificateStore:         LoadCertificateStore("./testdata/onelogin_cert.pem"),
				AudienceURI:                 "{audience}",
				SkipSignatureValidation:     false,
				AllowMissingAttributes:      true,
				Clock: dsig.NewFakeClock(clockwork.NewFakeClockAt(time.Date(2017, 3, 8, 7, 51, 0, 0, time.UTC))),
			},
		},
		{
			ScenarioName: "OracleAccessManager",
			Response:     LoadXMLResponse("./testdata/oam_response.xml"),
			ServiceProvider: &saml2.SAMLServiceProvider{
				IdentityProviderSSOURL:      "https://deaoam-dev02.jpl.nasa.gov:14101/oam/fed",
				IdentityProviderIssuer:      "https://deaoam-dev02.jpl.nasa.gov:14101/oam/fed",
				AssertionConsumerServiceURL: "http://127.0.0.1:5556/callback",
				IDPCertificateStore:         LoadCertificateStore("./testdata/oam_cert.pem"),
				AudienceURI:                 "JSAuth",
				SkipSignatureValidation:     false,
				AllowMissingAttributes:      true,
				Clock: dsig.NewFakeClock(clockwork.NewFakeClockAt(time.Date(2016, 12, 12, 16, 55, 0, 0, time.UTC))),
			},
		},
	}

	ExerciseProviderTestScenarios(t, scenarios)
}


================================================
FILE: providertests/testdata/adfs_idp_signing_cert.pem
================================================
-----BEGIN CERTIFICATE-----
MIIC2jCCAcKgAwIBAgIQNdNUIOmoKrFJNQadPV+9sTANBgkqhkiG9w0BAQsFADAp
MScwJQYDVQQDEx5BREZTIFNpZ25pbmcgLSBmcy5zcHN0ZXN0Mi5jb20wHhcNMTYx
MTAyMjIyOTE1WhcNMTcxMTAyMjIyOTE1WjApMScwJQYDVQQDEx5BREZTIFNpZ25p
bmcgLSBmcy5zcHN0ZXN0Mi5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
AoIBAQCjjUuNwckrCQgaXAMzHLFMuxrG+dREwct+d5W6PlAxlh8jxijhWXeHMcpP
naDLkPhI6J5hva+2r/kJcIuz3c9wAHVIl6oQdSeH4lwbrD/bfCnu0lOmjgey/cY2
lVTs4mfx1L7ejnUWd4ROfrHkhtFgqOYrJYir29ZOqojmCvaSQbqfjXAQmP8xvsFy
Yw0FNSURrt2ZZ9HTY1T6eJIrEJi0csgI4579IzYNyttwOfuM8rDrin2gTNTVTOE/
lfXv2q7CGU67ErETCtmaJJvlqYmRLwT51Jxrf3KLTkAjxyE4uISTuzys/Y/ktxI6
LQ3eFD/oRfTtA1V3pj96nxbxk2z3AgMBAAEwDQYJKoZIhvcNAQELBQADggEBAEZ5
uUC+Djrx9XJlrGHwwhVShyDcEvloJzeZLnNvMC1Gfyq4enHaVE5ztnkLTvphym9+
W9waFlVPHlIAXv3CKz9xbFWDtaXvD229pOGThDBCKo8o2vucHCo8gn+dUq0ZS+TV
LpqeNoTpjfI4FQZ7BwDoILza6xdXHmDukui1btNFYYwhHzgMx1x1cz4VvBj5D+WW
f13uClD2dkeasoZ4M9KH15Oy128tXhmzCw7ymsv3KC20CvHiqwGnYcybZdcIKQUT
aB3qCNW5ti44RxwmLF4tnuFONmPuH1Pet0Hnq6pWNBMsWNEPM9LRVoPLqDR3fmkL
/mNtZZd4xG3Kgd1IbPc=
-----END CERTIFICATE-----


================================================
FILE: providertests/testdata/adfs_response.b64
================================================
PHNhbWxwOlJlc3BvbnNlIElEPSJfYjlkM2VhNzAtMmEwYy00MmI2LWI4ZjctNjU3YWRlYjJiYjA5IiBWZXJzaW9uPSIyLjAiIElzc3VlSW5zdGFudD0iMjAxNy0wOS0yMVQyMzoyNzowNi44MjhaIiBEZXN0aW5hdGlvbj0iaHR0cHM6Ly9zYW1sLnRlc3Qubm9wZS9zZXNzaW9uL3Nzby9zYW1sL2Fjcy9ka25oeXN6amw3IiBDb25zZW50PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6Y29uc2VudDp1bnNwZWNpZmllZCIgSW5SZXNwb25zZVRvPSJfNTk4OGJmNDUtMWNjOC00MjI4LWIzZTgtMWFhODU5MGU2M2QzIiB4bWxuczpzYW1scD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnByb3RvY29sIj48SXNzdWVyIHhtbG5zPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXNzZXJ0aW9uIj5odHRwOi8vZnMuc3BzdGVzdDIuY29tL2FkZnMvc2VydmljZXMvdHJ1c3Q8L0lzc3Vlcj48c2FtbHA6U3RhdHVzPjxzYW1scDpTdGF0dXNDb2RlIFZhbHVlPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6c3RhdHVzOlN1Y2Nlc3MiIC8+PC9zYW1scDpTdGF0dXM+PEFzc2VydGlvbiBJRD0iX2ZkNjEwOGZkLWQyYmYtNDMyNy1hODFmLWMwM2I4ZmNhNzcwZCIgSXNzdWVJbnN0YW50PSIyMDE3LTA5LTIxVDIzOjI3OjA2LjgyOFoiIFZlcnNpb249IjIuMCIgeG1sbnM9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iPjxJc3N1ZXI+aHR0cDovL2ZzLnNwc3Rlc3QyLmNvbS9hZGZzL3NlcnZpY2VzL3RydXN0PC9Jc3N1ZXI+PGRzOlNpZ25hdHVyZSB4bWxuczpkcz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnIyI+PGRzOlNpZ25lZEluZm8+PGRzOkNhbm9uaWNhbGl6YXRpb25NZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzEwL3htbC1leGMtYzE0biMiIC8+PGRzOlNpZ25hdHVyZU1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMDQveG1sZHNpZy1tb3JlI3JzYS1zaGEyNTYiIC8+PGRzOlJlZmVyZW5jZSBVUkk9IiNfZmQ2MTA4ZmQtZDJiZi00MzI3LWE4MWYtYzAzYjhmY2E3NzBkIj48ZHM6VHJhbnNmb3Jtcz48ZHM6VHJhbnNmb3JtIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnI2VudmVsb3BlZC1zaWduYXR1cmUiIC8+PGRzOlRyYW5zZm9ybSBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMTAveG1sLWV4Yy1jMTRuIyIgLz48L2RzOlRyYW5zZm9ybXM+PGRzOkRpZ2VzdE1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMDQveG1sZW5jI3NoYTI1NiIgLz48ZHM6RGlnZXN0VmFsdWU+cmJTd3EwZVZTWXFYdDFwQ095SThtT255K2h1Ukt3OU1LR0dxS21BUnFxcz08L2RzOkRpZ2VzdFZhbHVlPjwvZHM6UmVmZXJlbmNlPjwvZHM6U2lnbmVkSW5mbz48ZHM6U2lnbmF0dXJlVmFsdWU+Wm96L3dldWlEckVBVHEvVXFGb3hLbTUySUZmV2NZbzM1S1BxY0ttZE9RaW9TcWdnYnJlbElzM3dFSUFveUdxZHZKd1ZSNmlac2RsNlpTU0g0Z29nV01ydFV6QXRVT2IvMjVJMU1HWVBNZmJ4bVZTL0syQ0RmSUllV2wzU1hIT0o0YWxURldtR21wQ2NrZFpvUUgvR3M4ZUFzOHBxOXBLWUVlZFhkVzNSc0JpZTRzSFo3ME94aGhZS0VWY0RrUkJkQzNHQ1pqSk4yY3RENUJZeGhJMDVENnN1eU9rYlpHYVBCR1l2MkFzSDRDTmRXeHlaVDhEN21tejM3NElNOWorcUQ2U3RuNFZ0STZXRThCb3ByV1daSWRnYzZCRDgwSEhQR0k1WGo5UzZzRmxsSEtaSGlRckVtNnd4WnMyUVQ5T0JiMVNRcjQrMTNUSFgxWkpBbFUwMTZBPT08L2RzOlNpZ25hdHVyZVZhbHVlPjxLZXlJbmZvIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjIj48ZHM6WDUwOURhdGE+PGRzOlg1MDlDZXJ0aWZpY2F0ZT5NSUlDMmpDQ0FjS2dBd0lCQWdJUU5kTlVJT21vS3JGSk5RYWRQVis5c1RBTkJna3Foa2lHOXcwQkFRc0ZBREFwTVNjd0pRWURWUVFERXg1QlJFWlRJRk5wWjI1cGJtY2dMU0JtY3k1emNITjBaWE4wTWk1amIyMHdIaGNOTVRZeE1UQXlNakl5T1RFMVdoY05NVGN4TVRBeU1qSXlPVEUxV2pBcE1TY3dKUVlEVlFRREV4NUJSRVpUSUZOcFoyNXBibWNnTFNCbWN5NXpjSE4wWlhOME1pNWpiMjB3Z2dFaU1BMEdDU3FHU0liM0RRRUJBUVVBQTRJQkR3QXdnZ0VLQW9JQkFRQ2pqVXVOd2NrckNRZ2FYQU16SExGTXV4ckcrZFJFd2N0K2Q1VzZQbEF4bGg4anhpamhXWGVITWNwUG5hRExrUGhJNko1aHZhKzJyL2tKY0l1ejNjOXdBSFZJbDZvUWRTZUg0bHdickQvYmZDbnUwbE9tamdleS9jWTJsVlRzNG1meDFMN2VqblVXZDRST2ZySGtodEZncU9ZckpZaXIyOVpPcW9qbUN2YVNRYnFmalhBUW1QOHh2c0Z5WXcwRk5TVVJydDJaWjlIVFkxVDZlSklyRUppMGNzZ0k0NTc5SXpZTnl0dHdPZnVNOHJEcmluMmdUTlRWVE9FL2xmWHYycTdDR1U2N0VyRVRDdG1hSkp2bHFZbVJMd1Q1MUp4cmYzS0xUa0FqeHlFNHVJU1R1enlzL1kva3R4STZMUTNlRkQvb1JmVHRBMVYzcGo5Nm54YnhrMnozQWdNQkFBRXdEUVlKS29aSWh2Y05BUUVMQlFBRGdnRUJBRVo1dVVDK0Rqcng5WEpsckdId3doVlNoeURjRXZsb0p6ZVpMbk52TUMxR2Z5cTRlbkhhVkU1enRua0xUdnBoeW05K1c5d2FGbFZQSGxJQVh2M0NLejl4YkZXRHRhWHZEMjI5cE9HVGhEQkNLbzhvMnZ1Y0hDbzhnbitkVXEwWlMrVFZMcHFlTm9UcGpmSTRGUVo3QndEb0lMemE2eGRYSG1EdWt1aTFidE5GWVl3aEh6Z014MXgxY3o0VnZCajVEK1dXZjEzdUNsRDJka2Vhc29aNE05S0gxNU95MTI4dFhobXpDdzd5bXN2M0tDMjBDdkhpcXdHblljeWJaZGNJS1FVVGFCM3FDTlc1dGk0NFJ4d21MRjR0bnVGT05tUHVIMVBldDBIbnE2cFdOQk1zV05FUE05TFJWb1BMcURSM2Zta0wvbU50WlpkNHhHM0tnZDFJYlBjPTwvZHM6WDUwOUNlcnRpZmljYXRlPjwvZHM6WDUwOURhdGE+PC9LZXlJbmZvPjwvZHM6U2lnbmF0dXJlPjxTdWJqZWN0PjxOYW1lSUQgRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoxLjE6bmFtZWlkLWZvcm1hdDplbWFpbEFkZHJlc3MiPnBhdWxAc3BzdGVzdDIuY29tPC9OYW1lSUQ+PFN1YmplY3RDb25maXJtYXRpb24gTWV0aG9kPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6Y206YmVhcmVyIj48U3ViamVjdENvbmZpcm1hdGlvbkRhdGEgSW5SZXNwb25zZVRvPSJfNTk4OGJmNDUtMWNjOC00MjI4LWIzZTgtMWFhODU5MGU2M2QzIiBOb3RPbk9yQWZ0ZXI9IjIwMTctMDktMjFUMjM6MzI6MDYuODI4WiIgUmVjaXBpZW50PSJodHRwczovL3NhbWwudGVzdC5ub3BlL3Nlc3Npb24vc3NvL3NhbWwvYWNzL2Rrbmh5c3pqbDciIC8+PC9TdWJqZWN0Q29uZmlybWF0aW9uPjwvU3ViamVjdD48Q29uZGl0aW9ucyBOb3RCZWZvcmU9IjIwMTctMDktMjFUMjM6Mjc6MDYuODI2WiIgTm90T25PckFmdGVyPSIyMDE3LTA5LTIyVDAwOjI3OjA2LjgyNloiPjxBdWRpZW5jZVJlc3RyaWN0aW9uPjxBdWRpZW5jZT5odHRwczovL3NhbWwudGVzdC5ub3BlL3Nlc3Npb24vc3NvL3NhbWwvc3BlbnRpdHlpZC9ka25oeXN6amw3PC9BdWRpZW5jZT48L0F1ZGllbmNlUmVzdHJpY3Rpb24+PC9Db25kaXRpb25zPjxBdHRyaWJ1dGVTdGF0ZW1lbnQ+PEF0dHJpYnV0ZSBOYW1lPSJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9naXZlbm5hbWUiPjxBdHRyaWJ1dGVWYWx1ZT5wYXVsPC9BdHRyaWJ1dGVWYWx1ZT48L0F0dHJpYnV0ZT48QXR0cmlidXRlIE5hbWU9Imh0dHA6Ly9zY2hlbWFzLnhtbHNvYXAub3JnL3dzLzIwMDUvMDUvaWRlbnRpdHkvY2xhaW1zL3N1cm5hbWUiPjxBdHRyaWJ1dGVWYWx1ZT5mcmFsZXk8L0F0dHJpYnV0ZVZhbHVlPjwvQXR0cmlidXRlPjwvQXR0cmlidXRlU3RhdGVtZW50PjxBdXRoblN0YXRlbWVudCBBdXRobkluc3RhbnQ9IjIwMTctMDktMjFUMjM6Mjc6MDYuNzY3WiIgU2Vzc2lvbkluZGV4PSJfZmQ2MTA4ZmQtZDJiZi00MzI3LWE4MWYtYzAzYjhmY2E3NzBkIj48QXV0aG5Db250ZXh0PjxBdXRobkNvbnRleHRDbGFzc1JlZj51cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YWM6Y2xhc3NlczpQYXNzd29yZFByb3RlY3RlZFRyYW5zcG9ydDwvQXV0aG5Db250ZXh0Q2xhc3NSZWY+PC9BdXRobkNvbnRleHQ+PC9BdXRoblN0YXRlbWVudD48L0Fzc2VydGlvbj48L3NhbWxwOlJlc3BvbnNlPg==


================================================
FILE: providertests/testdata/adfs_response_enc.b64
================================================
PHNhbWxwOlJlc3BvbnNlIElEPSJfYWUyNDZkNGUtYjllMC00OWUzLTk3YTItYzJmMjM2NDA4YjllIiBWZXJzaW9uPSIyLjAiIElzc3VlSW5zdGFudD0iMjAxNy0wOS0yMVQyMzoxOToxMS42MDhaIiBEZXN0aW5hdGlvbj0iaHR0cHM6Ly9zYW1sLnRlc3Qubm9wZS9zZXNzaW9uL3Nzby9zYW1sL2Fjcy9ka25oeXN6amw3IiBDb25zZW50PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6Y29uc2VudDp1bnNwZWNpZmllZCIgSW5SZXNwb25zZVRvPSJfMjUyZDAxMjktMWM4My00OTZlLWIwMzktZmZjOWNlZTExY2NhIiB4bWxuczpzYW1scD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnByb3RvY29sIj48SXNzdWVyIHhtbG5zPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXNzZXJ0aW9uIj5odHRwOi8vZnMuc3BzdGVzdDIuY29tL2FkZnMvc2VydmljZXMvdHJ1c3Q8L0lzc3Vlcj48c2FtbHA6U3RhdHVzPjxzYW1scDpTdGF0dXNDb2RlIFZhbHVlPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6c3RhdHVzOlN1Y2Nlc3MiIC8+PC9zYW1scDpTdGF0dXM+PEVuY3J5cHRlZEFzc2VydGlvbiB4bWxucz0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFzc2VydGlvbiI+PHhlbmM6RW5jcnlwdGVkRGF0YSBUeXBlPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGVuYyNFbGVtZW50IiB4bWxuczp4ZW5jPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGVuYyMiPjx4ZW5jOkVuY3J5cHRpb25NZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGVuYyNhZXMyNTYtY2JjIiAvPjxLZXlJbmZvIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjIj48ZTpFbmNyeXB0ZWRLZXkgeG1sbnM6ZT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8wNC94bWxlbmMjIj48ZTpFbmNyeXB0aW9uTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8wNC94bWxlbmMjcnNhLW9hZXAtbWdmMXAiPjxEaWdlc3RNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjc2hhMSIgLz48L2U6RW5jcnlwdGlvbk1ldGhvZD48S2V5SW5mbz48ZHM6WDUwOURhdGEgeG1sbnM6ZHM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyMiPjxkczpYNTA5SXNzdWVyU2VyaWFsPjxkczpYNTA5SXNzdWVyTmFtZT5DTj1zYW1sLnRlc3Qubm9wZSwgTz1TQU1MIFRlc3QgTm9wZS4sIFM9Q2FsaWZvcm5pYSwgQz1VUzwvZHM6WDUwOUlzc3Vlck5hbWU+PGRzOlg1MDlTZXJpYWxOdW1iZXI+MTY5OTkyMzEzMjE5MTk1MjQ4NDM8L2RzOlg1MDlTZXJpYWxOdW1iZXI+PC9kczpYNTA5SXNzdWVyU2VyaWFsPjwvZHM6WDUwOURhdGE+PC9LZXlJbmZvPjxlOkNpcGhlckRhdGE+PGU6Q2lwaGVyVmFsdWU+Qk43OVlidmYxaERHdDArT1lPY2lRVjVDWk53c3krZ2dtSnFmYjhRTXBVZVVJc2JIT3FPOUxpK3FGQmpZbjBpZmZXc0Q4TXhxdlVjVWY5QzdSYlV2UjgvdEdIU3dHQWZEbzhrU3dCOENqTGhjLzgyYTkweTNDSkI5eGxOZHFyek54eERueXM0NS9iQ0Y5L3I3U1VwbG1tU0NJcTdzQUxjT3hxRnh6VWZ0Q2xxcWlvUS9EbVA2RkZWWEx1ZXNRaHgya0p4aGYzL1RQUW5HZTNMYjNFSm1lZnh0SVFiSXJpRm95M1poUUVLRXlialFWTDFaWHAzanV3QzJVZDU2ZEt3d21lcDY4R3JtUU5xdE1IYW9qUEtmRVpnRExXVlhCZnlHWmx6NnBjRlYvQ1pJNFVySnd2TTlObUlxT0VsbHQ2S3JWdUVhaG5xT2lCeDdrNi9sZ2IxY0lnPT08L2U6Q2lwaGVyVmFsdWU+PC9lOkNpcGhlckRhdGE+PC9lOkVuY3J5cHRlZEtleT48L0tleUluZm8+PHhlbmM6Q2lwaGVyRGF0YT48eGVuYzpDaXBoZXJWYWx1ZT5ETHlXS3F3UmlMbnpzcnk0eVlRYURjNEtONEt4bjhmV2dwc0hSWXlSaUczbzRoenJlOTdXV3o5WGNRRUpSVGlqVUMvdnEwR1pSUXRuTU5ycTMxLzJPNTN6cUMwbzhnUG9Gb28rTTZEMUxOSURTYzBRdFpiY2xUWmY5Z3o4dkVwNDdDSDJDTVNkUzAvVmxremJHZ0w1UHlkUlNQcGcrb1FiUVJJWThJWHBPd0RhdUJlMElidjV0QURUR0k3WkxTb0RmblFSUVluMWZySUx5SkVmMWU5MU1IODRzZWt0NEEvNDQrRFBtc0JjKzdWN095WGk3N2hBcnRDeEN5REowaUJZb1QwSkZWK1RhNUFSMkFKRk1HanNVL3QrUTNXdGhuRzZUbVFtYVpYVXFzem5FUGptWXp5eUNSM2ZoSE9pdmZGK09GYms0Nzk5WmtiY2ZNN1lvL2FZR3pMSHllM25ZNThCTFlCbnhFR0VCcDhacGhsWSs2VUgxYlF5V1Fid09tcjRJQk03ejNLaCtQS3pBQThYbDNQcG9CSHl4VkVXWC94S1BvQWtTV2FXcmRFZy8vZ1kwZURybUVwOHlZM0pjRXU4VkNPTmZKRXlIbjBabXdSVTdLd0lWMDFmTk1kVDdVbUJ1RWtucE55eUNhc2FTMm9zOUM0RlhmWVUzU3pFOXhXM1VUZkticE1OMW1zSnVGaXdPcXNIRzF0MklHdVZTV1puZVNrQTdDYUVvUFNLVHJNVGw3OERqVGQ0dW1WbEVmajZnRXg1S3FnU092ejBicTR4NGdURVVHL1JkNFlFZUhWNTZ6Rm5tOHVZVVRDTUNHS2kzYlk3MndXdDMvb1dVeTlhUk5nQmI0YjZKR0ZzRUx4OS91aEdYUlJNRjhad0t2WXBpSHJ1Q3pybk1zeDVWTFM5dkhIZ0RnekpvbFJSbjl2aE1aeXJ4SDMwTVpobk1JMEdTbDVmeXdzdjNXejcxNDJJOUxiWkxOSEpWdTNJTTMrQVF0a0M3R280Mmc2eVB0ZmRMQklwMnlvOWhlVlp6UTFNZUp6MUQyT3EvM2RqN1JFZDJHcGNSRnpTb25jc0VuU0ZiRlZQSU5tdmdBTXhYaU5LTmJ1N0ZZOUdSblorM1FaUnUzRmRLY1N6V0tVeFduT05oNFRYN09WNjIrNWZKQm1sN3RZTmRKZTBOblpQS1I3VlBsSmpBTnBNd0NoekEzaFRKUzJFTmY0RUtVS2xHaW8vUXVmWmxDOEhTUzhlM0djeGtub0F0V2YwVmdZWmZYZDNMWGxrSnV5NHRCMC9SNm1sYnh1WlBaOEZXWXBCSzF6RVpHbDN2L0pNbDAya3cvVzh4bHB0enZkNE9tcStOUTcrbG1TZHdZWXhpeE8xaG9GSUh2WmpYdlhHazNONGZNZzQweVNIRG1wejJNNWJBSi81aXNqTWo5cERzczh1Lzd2bGtzR1pFaVIvdE04UDBJNEZlUFE5ODlXaStKOVdiWjV3MGtCaWRKKzRNVXBKY0hUd1pLSHI1NEpTOTVBeHVkWjNCY2hLSGhnQXVpTVU0S3VGRWpqUDg4d0RoaFBBWHdnd3pzT1A0RDh5LzlTaVBnclRUYjFoUTVPWXFZU040ZzMvY0YveDFIVmRzWkZodUZ5Q2JBQjgxTm1tVG5FR1dOd25qemgvcGVPQzNYa05UUGx6Yzhpd284bVdqU3dibGZBZHphL2xnQ0xhODhsdDZqd0ozUDQ4c1hqRVZyWjhob1B1RE9hQnc0bVh6QjN0NEtVeEJmR0JjRzZxczUzUVQ3YjVNRnduamVIVjFETldVV05Fa0FZbEl2MFhJeWZ1MUJwWHZ0M1d1MmNoTys2RDg2aDVoclZGVzhHcFdFa3lSaGUvSHk3RGRURHZEMURUWVlIUXBSR2EwZnNxUVF1Z1NyT2hMdmxTWFltbmY0ZnorcWVNK1JucU1NMi8zNi84eEVIc1dzWDRtNVhlSWV1d2NrK2lXQ0ErTklRTzFodU9UU29qQmtMeXh5ZHZnU3lXWDZER2hNZlZPZWdKbWZrNjMxUlZBT1F4OWpVY0FhRklaa3RIS3JzUklKb3RUSytiZ1BwSTNmbzh3cEhjaDZiSmJTZ1U0ZGtNWmh2Sm9yM2J4bk5qcFpjZWVNWHlKdmg2TUZsZ1ZVSXNVTG12cGhxQldhWFg0TlZEcVNCZlFhbFVydGFYVW8rTzhVTy9pZWpKdGdBVTl2Yi9pWElsemJnZ1lUTmdIQmJVbk4zdTFyb2pCU0pDYnNvNDJZK013b0MxbURTRjJKbTkxTlhXR2hyUjZzZ0xmOTlBUTlGSmNGNjhuWkNmT0MzTkJPVG9KOVBLZEJzT1AvRzRPSlBmWWN0VWwwVC90alFXQS92L0ZqRHJrclZpUWttOUY4ak5lUTZISDVDZGl4MmJMY0FqTk1kVmVTMGticXVmRFBaMWY1bTJOYXRnclpwU1dVTk1vSXd4YkovVFJkVENYWDZmY0NkS0laa25XbXRHVHdudWU5cVVUS0cvS0krZkdUakcwSS81czdIb1pkZ1llS2xDY2poc2VXU1VSakdIb0tDU1FXRFAwOUxvYkQvSlN4UWtlTm9IdS9BR0ovMnFJMnJ2dCtKWEh3SmpxMDNIZStvNEQva1Vwc1BLRDZtbWdWU0dVSlA0UTZRcjhXbm1McTA4WEdGRTN4WFM1NjVESEd0UmVrTEJobDN1eFMwcHRobkUvSyswbGtLdzQzWmpZTVEydUtiTURIYWJQa1Y0bUpoZXFVQllxZGN2VmJEZ2d5TStFclp5NFJtQTlPcTgrTFMxenljRlVKcmJtKzQ2TXo1aXV4NzJMYWFGNEZMRjR2ZDBTbnAxc05GZmxPK3Y5UVpkc3dMNHdQa3ZSL3VYYWxOQ1k4QUZ4ZzhjU2p0RHUxbnN0bmV5MzA0c3NXRytFTmN5T0ZCZExUWFJqMlMxSDFlWEd2c0pYMGdrMmFRL1FhMmdReGlHcmJKK2ROeWgwVUp2NGJ3N2NXejJQTWhEdEx4bnU5ZFFZL0JrcXppcm5McUUrNUE1SzVwRUlJVXJLeTlnZXRpQzR4cDAyazZlQnh0dmFSVTQ4bkZ2RGdIY3BUdEplTmVhK3orZm9tR2UzdkNLcHp2a21CYU54dElqRWRrdEtrRmFaeE92L2R5ZGEvOVdMNERvTjFQa24xSkJxbzhOM0ZnQWNtVjlzZkpqbGcxRVpiSWgwT2lEWWY3aXFqY3BlRHRwbjNYOWJ2dXYwSkRQaC9FTExaYTJNaC9tSWJ2L21SMG8vdzEzOWdtN09mVWlPR2paR1YyMjl1TVBmOFlUNXlURjgyb3lqb2QrTEZCLzEvSGI5VWdiT1VyUGZJbVo1TnJrU2tTRUt1M28xUlRMc21TRS9VeXJISlhhRC80dFZubVJ2UWhiQ3ViVmUzeHdLTUZUbitNeG5IUUN3M2E3UFAycElwVEpDOGVCZlE0Um1xSE4ycXNNcFdEMitHWi9xb2Z1SU1tNTRXYjBMTGt1NnA3QW9hRExJVTYwSC9XL1I2U2VvbmVQWE1pL3ZTeThOamhoK214b08wMEVLYWNDRnhDT05vZUN0N2J2eWhOUWFPbklUbTcyT1ZHT0dMaDlxRkFyYUNTRlRSWC9QVFdWV3N1QWtaNUVFUWpkN2xSdVMxWUc2ZXFFMGdVQWxWckFTZTJSRXZvejZ1RnFCQ3JlYUovUDBIUWpBa2dnV1hDU3ZGOVNaMm1mSTgvd1haYkpNaDVBNVduUW9PdVYwMlJQNFYzbmJCd0hzRFFibzBzRWcvaitkRlVIVTRpTEhFLzB6QTZZWmgwamtzOHhlN0pEbGI2cmo2M1EwRnZZMlM3Nnh4U1I4RlpkbEtxdXJQSjVXRWsxNmRSNUxQWkRSbkFpODBnUURrUkV4SllDZW1uS2pqeGpWSndld3pISzlIV2V2Q2xuYUxxWmxxbkRUendXWWZ2dkU5d09zYndNZ2VoRTFWWWFBNGFPUSs5T21udmgvemdHS215UXI2dGlSK3p0cjhSK0dHMy93SHlua2JRQWNIMjZkL09YaVRuWVZ0eXlGUk1TL1QxdVFJWnU1MkNjZGFoL2xSdVRTMjhNN1N1eFNpS1N5Q0pvc29YZDQ1VXZ4RVpNUi9DRGFpUmk5TUNKNE5OR0hxRlF0SFJhQmRuYmx2VmJNZ0lXYnFma1UrK2ZwNWVIaHJBa296eHRKU2RIeU9JWUNWMlM5T0ZRaGR2S3hzNkFzSzZHYm9xV3ZpbFdPMVoxOHZzTXpSd29wcks4RU04Y3ZmMEh1enJaZ1lFVGlGVVRJLzlaMHB3U1djVWNnSEJBUUVzZnJWYkVtc2lyQ3dvRnFSZmthZ00zSmUrQncyRk11NlZXNHI2cVJuT2Z6cS8zZkZmZVpjemdNN1VBVWVMT091L3ZKUmF6R0Z0L0ROQksvSTJjcEZ0UW0xazNuQS9WYmtDQ0plaGFMS1FRa3hVNFlMV1R2bnUzdWRxaVlHQlNsOXBjLzBJV3ZicUlHS3Z2ZHhiMjNPUFZUZGVVMnpndmF0eVk5cHFDVE5rMUtMV3d4TDVQREJ5VythdGRSNk1GK2o4Rk1mWUg4S0RlRWcwV2lrK3VqU2dBTWRUc0tKcEF0UGkxMnZ2M2FMZzVINnplSHdBNEZUN2M5ZUZsaVdGTWlyc2FHcTlGNENpTzdFZnY1QWFpK2YyUXNyWDlxUFRncExCbzZHUFM5cUhYb2tkV283U0tTc084OGs3cDJnTWdiN1hoTkF1RHg4RElVWDQ1MnBDTXV6OXhCRnBpTWx0OW4xc1hQNjY2REkxS2RjMEprbnVvRlVBalVySk9Rb2xJL1QySVZYcjFiYVh5TUc1dUE0bTNvQXBveDU5S2pMYmh0ZTQxNXJuTzlBMVBCU2wwVlN1YjhBL0MwaHhaK3NGZXUwRUNoc3pHbGY0amdNQStZd2U4TVdud0F0TVM3L1hiZ0tkYUt1QXpoNFBuNzdWVlFSNThkM0FmL2lneEdsSE5JRDhPVS9yWVREVkNrWjJVaEUzdHlDUmN5a3VzcUIvN290dEZVbGpsbEwxR1loaW9iMTkvVnQrZklIVHhWa2hlZTErQnp3cUxpeWlQdmE4OVZHbG9UTEo1UzY4US80N0FGODVneTM1U0ZBUnM1dUljT3NtWXN2Vm1qdUpOZU5Hd2g4SmpFbHIxR015N2Ywd3pITS8rVm1kbk9lSHBXUktMaXhrZEloOVVNcUdFV0pBNG9uQ3NWRlU2ZnpXdCtSTUdySlhseTF5VzdMTnRGUWtxaGxiWDlhY2RuTE9qSzc5clpNdmwwTjZjMk5OQmpHT01BbkZSWnZyd09sSmJ0cGhjMGFWQmNBcFUxNzR0aU01Ym5xWDBpV1NIai82VmRWY29yNmlMVlY2RER3TUpFUlNQeDJmd1hUVjdGTTRkamR3S2UzRmpZY1FCRmV1bXl0clVxWkZRQjk4T0xpS0ZOdi85ekdXUGxXOHVoUEdjYk9KeUxXaHl0UlUyekZPMGlOYnpDMTlUUnhnRUJSYUhDQmMyYThzWDVnZHRYeGt3L0hYU0Iyc1k3bnA1TzJZOHBYb21sSVFyd01senJpQkpTVnprOGZoMHpzS0d5UXZGR2p4Z0R6SjNHVGRwSjFiSWNUSkdidjM4bGNVZVBma2xzTURESUE5dDVBM1E3RFVCVHRSd2xTazNmdXJjOWRaY1EydlJ3WU5HT1RLaXZxSTNXRmdUamxBbHhNQVVkbURha1pzbmxycEFxNC8yOTNKdDYrK2pLejZ2RUlVK0FJc2doVzN1NmUrREN6MWNTL2tUZzJtZkRhUy9vZ2tMOWdKMVhYM2pvL3o1RTEzTGpZRmNScFVOTVlLbDBVOEZNeUJPWmxIa2E3L08xM3JScnh6ZG9DbzRMdExjVTRpZ1ZWc2dlUGhTVFFZVTRNRHo3RzdTYTRLeFZiK3lQU3JrNzVDQWpiQmhwU01DK044U285WDBYaGZzV3hLanE4OWlPUHNOMWMwcS8yTTBoZjBVR2ZFaUtpNEMwUUFUUWtuNWVLSzRqV1JJT05QckcxTXhHT29uV3VaVGNFYlArTkh6UjV5T0w5aWZOdlBwcFBuV1Yvc2RzZUd3MFdvK3A5NHA1V1oyMFpJT29QTjJzN1FEanY4elQ4V1pLcnZHQ0FjWVN3RktYdHVrZXFsQ3MvSzBhV090ZVc2NFNFeGNLKzlkM1JMY3BsTzh3TGEweDA3Wmx0SHRsbmRUcjR2emFLQUdKdHBTd3Q2UjA0WTJsdmk4NjJZNGtLeXk0MEVTeGJsS04zMktMZDNHSzdFSzNKRVZTTllMejdmZUhuczhBUmxpNEFrS0ZnQmZrODl2M3FKOG9SYXZVT3dOWUpjdlRWTEIzUU9VRzdLLzBjV0o8L3hlbmM6Q2lwaGVyVmFsdWU+PC94ZW5jOkNpcGhlckRhdGE+PC94ZW5jOkVuY3J5cHRlZERhdGE+PC9FbmNyeXB0ZWRBc3NlcnRpb24+PC9zYW1scDpSZXNwb25zZT4=


================================================
FILE: providertests/testdata/auth0_cert.pem
================================================
-----BEGIN CERTIFICATE-----
MIIC9DCCAdygAwIBAgIJX9Qb0a2w33UjMA0GCSqGSIb3DQEBBQUAMCExHzAdBgNV
BAMTFnNjYWxlZnQtdGVzdC5hdXRoMC5jb20wHhcNMTYwNzI1MTc0OTQ1WhcNMzAw
NDAzMTc0OTQ1WjAhMR8wHQYDVQQDExZzY2FsZWZ0LXRlc3QuYXV0aDAuY29tMIIB
IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAx+MnMtsJsNjDD4YEsi//LeBD
wbwqwYTJk8mUlTLuaSss2X4aPo2kGjuEjvTc0x4Mk9ZQ/CIPPX3J9N4AFQu88rRd
pT+TTuY9AIiyt8sZ+b1qF9eewyLVchM6s9Ff2JqgDIfajym825GKAJL7hi6smFRT
6h+OyyHo8pJjXePldum4woSFW/H3y83meF51Wn9oMTRRVAlpsdRVOgmuQpBlw3ap
eeRpAmqLYT7DH3j30umYu3+4+NcxtEC7s5+QAIenuORjjw+M+IB+HhYpy1AnblY6
4RRAD+EfESEC+AF/+G0zuA4q43s9IduYZp2GMzPzMlnhDlR5syiLGFtLZ+pwFQID
AQABoy8wLTAMBgNVHRMEBTADAQH/MB0GA1UdDgQWBBSX/v06GyNh1an1f7O7KKER
6xNntTANBgkqhkiG9w0BAQUFAAOCAQEAgtITYCbzRo8l8Q3+EHFwASlmnSyRm8HW
G41nmHLcC2lUqNvniSEf0kO65oMN2nxlq9JMeI7NxKNpTVL4OYX5+/0NqcxePayY
/9/5jBRwqF+84USc8HG8z+BFh1rVW35eoE5ULeegUBPgDG9shQjSLyIkNvYqXQ7A
tjMka8lkmdtU9XBlfYZC4YEmkeQOHkW5gmix5opajkj+Tih7HKsdhOgxZrl7/4Wm
GOoyR2q3Ffg8fmNgDe2Sf8bGv7IoTeeYVHIurVFoQSLziICOBQSpk/y/NS3HdSBO
ADRxhUn241jRTaEi06qcg/A9P4zulKR61mCVpEWv6ZveEyVm4XA4xQ==
-----END CERTIFICATE-----


================================================
FILE: providertests/testdata/auth0_response.xml
================================================
<samlp:Response xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" ID="_5376783a08fa6e021aa4" InResponseTo="_e3ce5e05-4e53-44ff-9229-c649f2b859a0" Version="2.0" IssueInstant="2016-07-25T18:29:17Z" Destination="http://localhost:8080/v1/_saml_callback"><saml:Issuer xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">urn:scaleft-test.auth0.com</saml:Issuer><samlp:Status><samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/></samlp:Status><saml:Assertion xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" Version="2.0" ID="_mU52Pie5AsaLMC1ne4sCHEYWPjvt00oS" IssueInstant="2016-07-25T18:29:17.523Z"><saml:Issuer>urn:scaleft-test.auth0.com</saml:Issuer><saml:Subject><saml:NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified">google-oauth2|117637692321743777825</saml:NameID><saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer"><saml:SubjectConfirmationData NotOnOrAfter="2016-07-25T19:29:17.523Z" Recipient="http://localhost:8080/v1/_saml_callback" InResponseTo="_e3ce5e05-4e53-44ff-9229-c649f2b859a0"/></saml:SubjectConfirmation></saml:Subject><saml:Conditions NotBefore="2016-07-25T18:29:17.523Z" NotOnOrAfter="2016-07-25T19:29:17.523Z"><saml:AudienceRestriction><saml:Audience>urn:scaleft-test.auth0.com</saml:Audience></saml:AudienceRestriction></saml:Conditions><saml:AttributeStatement xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><saml:Attribute Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier"><saml:AttributeValue xsi:type="xs:anyType">google-oauth2|117637692321743777825</saml:AttributeValue></saml:Attribute><saml:Attribute Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"><saml:AttributeValue xsi:type="xs:anyType">russell.haering@scaleft.com</saml:AttributeValue></saml:Attribute><saml:Attribute Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name"><saml:AttributeValue xsi:type="xs:anyType">Russell Haering</saml:AttributeValue></saml:Attribute><saml:Attribute Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname"><saml:AttributeValue xsi:type="xs:anyType">Russell</saml:AttributeValue></saml:Attribute><saml:Attribute Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname"><saml:AttributeValue xsi:type="xs:anyType">Haering</saml:AttributeValue></saml:Attribute><saml:Attribute Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn"><saml:AttributeValue xsi:type="xs:anyType">russell.haering@scaleft.com</saml:AttributeValue></saml:Attribute><saml:Attribute Name="http://schemas.auth0.com/identities/default/provider"><saml:AttributeValue xsi:type="xs:anyType">google-oauth2</saml:AttributeValue></saml:Attribute><saml:Attribute Name="http://schemas.auth0.com/identities/default/connection"><saml:AttributeValue xsi:type="xs:anyType">google-oauth2</saml:AttributeValue></saml:Attribute><saml:Attribute Name="http://schemas.auth0.com/identities/default/isSocial"><saml:AttributeValue xsi:type="xs:anyType">true</saml:AttributeValue></saml:Attribute><saml:Attribute Name="http://schemas.auth0.com/email_verified"><saml:AttributeValue xsi:type="xs:anyType">true</saml:AttributeValue></saml:Attribute><saml:Attribute Name="http://schemas.auth0.com/picture"><saml:AttributeValue xsi:type="xs:anyType">https://lh3.googleusercontent.com/-XdUIqdMkCWA/AAAAAAAAAAI/AAAAAAAAAAA/4252rscbv5M/photo.jpg</saml:AttributeValue></saml:Attribute><saml:Attribute Name="http://schemas.auth0.com/locale"><saml:AttributeValue xsi:type="xs:anyType">en</saml:AttributeValue></saml:Attribute><saml:Attribute Name="http://schemas.auth0.com/clientID"><saml:AttributeValue xsi:type="xs:anyType">rlXOZ4kOUTQaTV8icSXrfZUd1qtD1NhK</saml:AttributeValue></saml:Attribute><saml:Attribute Name="http://schemas.auth0.com/updated_at"><saml:AttributeValue xsi:type="xs:anyType">Mon Jul 25 2016 18:29:17 GMT+0000 (UTC)</saml:AttributeValue></saml:Attribute><saml:Attribute Name="http://schemas.auth0.com/nickname"><saml:AttributeValue xsi:type="xs:anyType">russell.haering</saml:AttributeValue></saml:Attribute><saml:Attribute Name="http://schemas.auth0.com/identities"><saml:AttributeValue xsi:type="xs:anyType">[object Object]</saml:AttributeValue></saml:Attribute><saml:Attribute Name="http://schemas.auth0.com/created_at"><saml:AttributeValue xsi:type="xs:anyType">Mon Jul 25 2016 17:51:26 GMT+0000 (UTC)</saml:AttributeValue></saml:Attribute></saml:AttributeStatement><saml:AuthnStatement AuthnInstant="2016-07-25T18:29:17.523Z" SessionIndex="_PpuqwkgWTWGbYyLu3lhPMW4vvzLKIiEF"><saml:AuthnContext><saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified</saml:AuthnContextClassRef></saml:AuthnContext></saml:AuthnStatement></saml:Assertion><Signature xmlns="http://www.w3.org/2000/09/xmldsig#"><SignedInfo><CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/><SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/><Reference URI="#_5376783a08fa6e021aa4"><Transforms><Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/><Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/></Transforms><DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/><DigestValue>WpFHrL2L9I0IcJDC4cFrh54kqNM=</DigestValue></Reference></SignedInfo><SignatureValue>j6jV5RJuDF+TVy2obGLcl1nGpNPXsIyjud4NXTSStVibuFvJ8VetYU9yv9MlyhyI9GC9pL73zHt3havhm3iCvwBcOLcgu9oX+s60N5HUt7eVnEBUdXt52hEZki61D3w+//e6l6gt5Di9109vLHfV1KgkuKPC3IfBK8Z52rL6KfmIrry5OEl0n2o0A8wAYDnZkkY07LTYvZFcQ2JQ/plYEi/y+4yGYSxhjcVyPs/h0xavjk7xFJ0b16kUshpYUSkZOUOamW2tdlADXOrossOV4iOCOB2VnT5WjOfviYzW2WE39fmiZ7ahSmxCCtsuHS/xhRq05mi4j3EDd3N/07isVg==</SignatureValue><KeyInfo><X509Data><X509Certificate>MIIC9DCCAdygAwIBAgIJX9Qb0a2w33UjMA0GCSqGSIb3DQEBBQUAMCExHzAdBgNVBAMTFnNjYWxlZnQtdGVzdC5hdXRoMC5jb20wHhcNMTYwNzI1MTc0OTQ1WhcNMzAwNDAzMTc0OTQ1WjAhMR8wHQYDVQQDExZzY2FsZWZ0LXRlc3QuYXV0aDAuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAx+MnMtsJsNjDD4YEsi//LeBDwbwqwYTJk8mUlTLuaSss2X4aPo2kGjuEjvTc0x4Mk9ZQ/CIPPX3J9N4AFQu88rRdpT+TTuY9AIiyt8sZ+b1qF9eewyLVchM6s9Ff2JqgDIfajym825GKAJL7hi6smFRT6h+OyyHo8pJjXePldum4woSFW/H3y83meF51Wn9oMTRRVAlpsdRVOgmuQpBlw3apeeRpAmqLYT7DH3j30umYu3+4+NcxtEC7s5+QAIenuORjjw+M+IB+HhYpy1AnblY64RRAD+EfESEC+AF/+G0zuA4q43s9IduYZp2GMzPzMlnhDlR5syiLGFtLZ+pwFQIDAQABoy8wLTAMBgNVHRMEBTADAQH/MB0GA1UdDgQWBBSX/v06GyNh1an1f7O7KKER6xNntTANBgkqhkiG9w0BAQUFAAOCAQEAgtITYCbzRo8l8Q3+EHFwASlmnSyRm8HWG41nmHLcC2lUqNvniSEf0kO65oMN2nxlq9JMeI7NxKNpTVL4OYX5+/0NqcxePayY/9/5jBRwqF+84USc8HG8z+BFh1rVW35eoE5ULeegUBPgDG9shQjSLyIkNvYqXQ7AtjMka8lkmdtU9XBlfYZC4YEmkeQOHkW5gmix5opajkj+Tih7HKsdhOgxZrl7/4WmGOoyR2q3Ffg8fmNgDe2Sf8bGv7IoTeeYVHIurVFoQSLziICOBQSpk/y/NS3HdSBOADRxhUn241jRTaEi06qcg/A9P4zulKR61mCVpEWv6ZveEyVm4XA4xQ==</X509Certificate></X509Data></KeyInfo></Signature></samlp:Response>

================================================
FILE: providertests/testdata/oam_cert.pem
================================================
-----BEGIN CERTIFICATE-----
MIIB/jCCAWegAwIBAgIBCjANBgkqhkiG9w0BAQQFADAkMSIwIAYDVQQDExlkZWFv
YW0tZGV2MDIuanBsLm5hc2EuZ292MB4XDTE2MDYzMDA0NTQxNloXDTI2MDYyODA0
NTQxNlowJDEiMCAGA1UEAxMZZGVhb2FtLWRldjAyLmpwbC5uYXNhLmdvdjCBnzAN
BgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAht1N4lGdwUbl7YRyHwSCrnep6/e2I3+V
eue0pSA/DGn8OuR/udM8UCja5utqlqJdq200ox4b4Mpz0Jg9kMckALtKe+1DgeES
EIx9FpeuBdHlitYQNSbEr30HIG2nmeTOy4Vi5unBO54um3tNazcUTMA0/LJ6KQL8
LeZSlB/IxwUCAwEAAaNAMD4wDAYDVR0TAQH/BAIwADAPBgNVHQ8BAf8EBQMDB9gA
MB0GA1UdDgQWBBRYo1YjfrNonauLzj6/AsueWFGSszANBgkqhkiG9w0BAQQFAAOB
gQACq7GHK/Zsg0+qC0WWa2ZjmOXE6Dqk/xuooG49QT7ihABs7k9U27Fw3xKF6MkC
7pca1FwT82eZK1N3XKKpZe7Flu1fMKt2o/XSiBkDjWwUcChVnwGsUBe8hJFwFqg7
olNJn1kaVBJUqZIiXF9kS0d+1H55rStOd0CNXAzp9utr2A==
-----END CERTIFICATE-----

================================================
FILE: providertests/testdata/oam_response.xml
================================================
<samlp:Response xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:dsig="http://www.w3.org/2000/09/xmldsig#" xmlns:enc="http://www.w3.org/2001/04/xmlenc#" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:x500="urn:oasis:names:tc:SAML:2.0:profiles:attribute:X500" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" Destination="http://127.0.0.1:5556/callback" ID="id-IWlPTptSB-PlR80dwt8ZhVeG70mrz7nPvTVrhduK" InResponseTo="_e66b3a98-831c-4c96-5706-b63fe0549624" IssueInstant="2016-12-12T16:54:35Z" Version="2.0"><saml:Issuer Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity">https://deaoam-dev02.jpl.nasa.gov:14101/oam/fed</saml:Issuer><samlp:Status><samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/></samlp:Status><saml:Assertion ID="id-rT9rTqxdQC9j34YhVeNayUWC9EbIBgym6gp-MZt-" IssueInstant="2016-12-12T16:54:35Z" Version="2.0"><saml:Issuer Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity">https://deaoam-dev02.jpl.nasa.gov:14101/oam/fed</saml:Issuer><dsig:Signature><dsig:SignedInfo><dsig:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/><dsig:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/><dsig:Reference URI="#id-rT9rTqxdQC9j34YhVeNayUWC9EbIBgym6gp-MZt-"><dsig:Transforms><dsig:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/><dsig:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/></dsig:Transforms><dsig:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/><dsig:DigestValue>z1HD/59hv6UOd5+jeG+ihaFWLgI=</dsig:DigestValue></dsig:Reference></dsig:SignedInfo><dsig:SignatureValue>I99oG5kiOfIgbXYa21z/TOmzftTkFnXe9ObhBNSKit9kAhT93apYROqqXv4Ax96P144Ld7ERX1hgJsytK8LC2874Pk7QrSNm4zvW3x0D4GR4lM06CvJK/EhIur3TrCUJDPigvyP7TJitheCyBejwt0x0lqNP/OzR3tMbAIMRoho=</dsig:SignatureValue></dsig:Signature><saml:Subject><saml:NameID Format="urn:oasis:names:tc:SAML:2.0:nameid-format:persistent" NameQualifier="https://deaoam-dev02.jpl.nasa.gov:14101/oam/fed" SPNameQualifier="JSAuth">pkieu</saml:NameID><saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer"><saml:SubjectConfirmationData InResponseTo="_e66b3a98-831c-4c96-5706-b63fe0549624" NotOnOrAfter="2016-12-12T16:59:35Z" Recipient="http://127.0.0.1:5556/callback"/></saml:SubjectConfirmation></saml:Subject><saml:Conditions NotBefore="2016-12-12T16:54:35Z" NotOnOrAfter="2016-12-12T16:59:35Z"><saml:AudienceRestriction><saml:Audience>JSAuth</saml:Audience></saml:AudienceRestriction></saml:Conditions><saml:AuthnStatement AuthnInstant="2016-12-12T16:54:10Z" SessionIndex="id-l3NCbxKoBfUZcuKhlotMuIF3ydgYJgGGG6BGTTU6" SessionNotOnOrAfter="2016-12-12T17:54:35Z"><saml:AuthnContext><saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport</saml:AuthnContextClassRef></saml:AuthnContext></saml:AuthnStatement></saml:Assertion></samlp:Response>

================================================
FILE: providertests/testdata/okta_cert.pem
================================================
-----BEGIN CERTIFICATE-----
MIIDpDCCAoygAwIBAgIGAVLIBhAwMA0GCSqGSIb3DQEBBQUAMIGSMQswCQYDVQQGEwJVUzETMBEG
A1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzENMAsGA1UECgwET2t0YTEU
MBIGA1UECwwLU1NPUHJvdmlkZXIxEzARBgNVBAMMCmRldi0xMTY4MDcxHDAaBgkqhkiG9w0BCQEW
DWluZm9Ab2t0YS5jb20wHhcNMTYwMjA5MjE1MjA2WhcNMjYwMjA5MjE1MzA2WjCBkjELMAkGA1UE
BhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28xDTALBgNV
BAoMBE9rdGExFDASBgNVBAsMC1NTT1Byb3ZpZGVyMRMwEQYDVQQDDApkZXYtMTE2ODA3MRwwGgYJ
KoZIhvcNAQkBFg1pbmZvQG9rdGEuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
mtjBOZ8MmhUyi8cGk4dUY6Fj1MFDt/q3FFiaQpLzu3/q5lRVUNUBbAtqQWwY10dzfZguHOuvA5p5
QyiVDvUhe+XkVwN2R2WfArQJRTPnIcOaHrxqQf3o5cCIG21ZtysFHJSo8clPSOe+0VsoRgcJ1aF4
2rODwgqRRZdO9Wh3502XlJ799DJQ23IC7XasKEsGKzJqhlRrfd/FyIuZT0sFHDKRz5snSJhm9gpN
uQlCmk7ONZ1sXqtt+nBIfWIqeoYQubPW7pT5GTc7wouWq4TCjHJiK9k2HiyNxW0E3JX08swEZi2+
LVDjgLzNc4lwjSYIj3AOtPZs8s606oBdIBni4wIDAQABMA0GCSqGSIb3DQEBBQUAA4IBAQBMxSkJ
TxkXxsoKNW0awJNpWRbU81QpheMFfENIzLam4Itc/5kSZAaSy/9e2QKfo4jBo/MMbCq2vM9TyeJQ
DJpRaioUTd2lGh4TLUxAxCxtUk/pascL+3Nn936LFmUCLxaxnbeGzPOXAhscCtU1H0nFsXRnKx5a
cPXYSKFZZZktieSkww2Oi8dg2DYaQhGQMSFMVqgVfwEu4bvCRBvdSiNXdWGCZQmFVzBZZ/9rOLzP
pvTFTPnpkavJm81FLlUhiE/oFgKlCDLWDknSpXAI0uZGERcwPca6xvIMh86LjQKjbVci9FYDStXC
qRnqQ+TccSu/B6uONFsDEngGcXSKfB+a
-----END CERTIFICATE-----

================================================
FILE: providertests/testdata/okta_response.xml
================================================
<?xml version="1.0" encoding="UTF-8"?><saml2p:Response xmlns:saml2p="urn:oasis:names:tc:SAML:2.0:protocol" Destination="http://localhost:8080/v1/_saml_callback" ID="id12433943337943699538801121" InResponseTo="_15f66d2d-628b-4d9b-a99e-089d8da862e1" IssueInstant="2016-07-25T23:20:14.859Z" Version="2.0" xmlns:xs="http://www.w3.org/2001/XMLSchema"><saml2:Issuer xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion" Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity">http://www.okta.com/exk659aytfMeNI49v0h7</saml2:Issuer><ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#"><ds:SignedInfo><ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/><ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/><ds:Reference URI="#id12433943337943699538801121"><ds:Transforms><ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/><ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"><ec:InclusiveNamespaces xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#" PrefixList="xs"/></ds:Transform></ds:Transforms><ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/><ds:DigestValue>ABeBWHP23nfnxsyUWE5d59IIqQeXgHGol36mjFvWcA4=</ds:DigestValue></ds:Reference></ds:SignedInfo><ds:SignatureValue>NfzCNa5SytP8OH0kq5yElIzhQrlAWdHWV6fdZA8+6SH8yrCPFMOwCsQRM0UriNDasPhodEQIRCzcZuaGNXqXiNXmEcoILXEFWsLPNg0dxHrrdbmKTz+QxKB+4PFAmgOwFIMMN7xwinMBJG3JEhBTjj8QRg9TbVUG/3GgTrlfzNpp9Db94nPOuhyMNStNGMFUEfCyMRQ5ZYK66ritnHFrMDBnu7oiCEV7xDIRf97kqHIDVenyntR56zDLu/ndCJfuP66Fahae1sU0U2bHJfM/64YWvI/OyywsNlZl1tANRXiNaKt6ukvDcz4CFI8aRER7RNbsEhinGMWxHUey0c3o5g==</ds:SignatureValue><ds:KeyInfo><ds:X509Data><ds:X509Certificate>MIIDpDCCAoygAwIBAgIGAVLIBhAwMA0GCSqGSIb3DQEBBQUAMIGSMQswCQYDVQQGEwJVUzETMBEG
A1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzENMAsGA1UECgwET2t0YTEU
MBIGA1UECwwLU1NPUHJvdmlkZXIxEzARBgNVBAMMCmRldi0xMTY4MDcxHDAaBgkqhkiG9w0BCQEW
DWluZm9Ab2t0YS5jb20wHhcNMTYwMjA5MjE1MjA2WhcNMjYwMjA5MjE1MzA2WjCBkjELMAkGA1UE
BhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28xDTALBgNV
BAoMBE9rdGExFDASBgNVBAsMC1NTT1Byb3ZpZGVyMRMwEQYDVQQDDApkZXYtMTE2ODA3MRwwGgYJ
KoZIhvcNAQkBFg1pbmZvQG9rdGEuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
mtjBOZ8MmhUyi8cGk4dUY6Fj1MFDt/q3FFiaQpLzu3/q5lRVUNUBbAtqQWwY10dzfZguHOuvA5p5
QyiVDvUhe+XkVwN2R2WfArQJRTPnIcOaHrxqQf3o5cCIG21ZtysFHJSo8clPSOe+0VsoRgcJ1aF4
2rODwgqRRZdO9Wh3502XlJ799DJQ23IC7XasKEsGKzJqhlRrfd/FyIuZT0sFHDKRz5snSJhm9gpN
uQlCmk7ONZ1sXqtt+nBIfWIqeoYQubPW7pT5GTc7wouWq4TCjHJiK9k2HiyNxW0E3JX08swEZi2+
LVDjgLzNc4lwjSYIj3AOtPZs8s606oBdIBni4wIDAQABMA0GCSqGSIb3DQEBBQUAA4IBAQBMxSkJ
TxkXxsoKNW0awJNpWRbU81QpheMFfENIzLam4Itc/5kSZAaSy/9e2QKfo4jBo/MMbCq2vM9TyeJQ
DJpRaioUTd2lGh4TLUxAxCxtUk/pascL+3Nn936LFmUCLxaxnbeGzPOXAhscCtU1H0nFsXRnKx5a
cPXYSKFZZZktieSkww2Oi8dg2DYaQhGQMSFMVqgVfwEu4bvCRBvdSiNXdWGCZQmFVzBZZ/9rOLzP
pvTFTPnpkavJm81FLlUhiE/oFgKlCDLWDknSpXAI0uZGERcwPca6xvIMh86LjQKjbVci9FYDStXC
qRnqQ+TccSu/B6uONFsDEngGcXSKfB+a</ds:X509Certificate></ds:X509Data></ds:KeyInfo></ds:Signature><saml2p:Status xmlns:saml2p="urn:oasis:names:tc:SAML:2.0:protocol"><saml2p:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/></saml2p:Status><saml2:Assertion xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion" ID="id12433943338016269283631347" IssueInstant="2016-07-25T23:20:14.859Z" Version="2.0" xmlns:xs="http://www.w3.org/2001/XMLSchema"><saml2:Issuer Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity" xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">http://www.okta.com/exk659aytfMeNI49v0h7</saml2:Issuer><ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#"><ds:SignedInfo><ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/><ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/><ds:Reference URI="#id12433943338016269283631347"><ds:Transforms><ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/><ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"><ec:InclusiveNamespaces xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#" PrefixList="xs"/></ds:Transform></ds:Transforms><ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/><ds:DigestValue>jwweWw9Jrdw3X28IpBEQgQ5I0mwOeStoOSso1hjtqkg=</ds:DigestValue></ds:Reference></ds:SignedInfo><ds:SignatureValue>BiFOSVvt5tIqMDwO5gcBehbTGaqe4S6gBmDxywqx0H1KL7vdz5v46L/0GxyfAZESwPu1zEMXSpt24wY+oTN2sMEuOAw2SK0OROucF3gWzYs6Uk7MtXg6uXq+jXRF76qdilWi5O2t270vwPYMOAG78C0DFhvtOA+aJI5Uc/SxbYPeN9/3/ymOhNNzZNSz8CfxwjhIGYjBao4mJd3Cb0I3N7ggHP9LhxUsRWDq7zWhKms0EOOfuiRw3VCdZh3E8wvbykos8M7Iy3m12XHK/JDJ2U88KPX2aMjgOrxBUBLwnySzzQ4+MPYGaWL6/4TQWp/NX2pm4L9rMuQguJj50/5p/A==</ds:SignatureValue><ds:KeyInfo><ds:X509Data><ds:X509Certificate>MIIDpDCCAoygAwIBAgIGAVLIBhAwMA0GCSqGSIb3DQEBBQUAMIGSMQswCQYDVQQGEwJVUzETMBEG
A1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzENMAsGA1UECgwET2t0YTEU
MBIGA1UECwwLU1NPUHJvdmlkZXIxEzARBgNVBAMMCmRldi0xMTY4MDcxHDAaBgkqhkiG9w0BCQEW
DWluZm9Ab2t0YS5jb20wHhcNMTYwMjA5MjE1MjA2WhcNMjYwMjA5MjE1MzA2WjCBkjELMAkGA1UE
BhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28xDTALBgNV
BAoMBE9rdGExFDASBgNVBAsMC1NTT1Byb3ZpZGVyMRMwEQYDVQQDDApkZXYtMTE2ODA3MRwwGgYJ
KoZIhvcNAQkBFg1pbmZvQG9rdGEuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
mtjBOZ8MmhUyi8cGk4dUY6Fj1MFDt/q3FFiaQpLzu3/q5lRVUNUBbAtqQWwY10dzfZguHOuvA5p5
QyiVDvUhe+XkVwN2R2WfArQJRTPnIcOaHrxqQf3o5cCIG21ZtysFHJSo8clPSOe+0VsoRgcJ1aF4
2rODwgqRRZdO9Wh3502XlJ799DJQ23IC7XasKEsGKzJqhlRrfd/FyIuZT0sFHDKRz5snSJhm9gpN
uQlCmk7ONZ1sXqtt+nBIfWIqeoYQubPW7pT5GTc7wouWq4TCjHJiK9k2HiyNxW0E3JX08swEZi2+
LVDjgLzNc4lwjSYIj3AOtPZs8s606oBdIBni4wIDAQABMA0GCSqGSIb3DQEBBQUAA4IBAQBMxSkJ
TxkXxsoKNW0awJNpWRbU81QpheMFfENIzLam4Itc/5kSZAaSy/9e2QKfo4jBo/MMbCq2vM9TyeJQ
DJpRaioUTd2lGh4TLUxAxCxtUk/pascL+3Nn936LFmUCLxaxnbeGzPOXAhscCtU1H0nFsXRnKx5a
cPXYSKFZZZktieSkww2Oi8dg2DYaQhGQMSFMVqgVfwEu4bvCRBvdSiNXdWGCZQmFVzBZZ/9rOLzP
pvTFTPnpkavJm81FLlUhiE/oFgKlCDLWDknSpXAI0uZGERcwPca6xvIMh86LjQKjbVci9FYDStXC
qRnqQ+TccSu/B6uONFsDEngGcXSKfB+a</ds:X509Certificate></ds:X509Data></ds:KeyInfo></ds:Signature><saml2:Subject xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion"><saml2:NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified">russellhaering</saml2:NameID><saml2:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer"><saml2:SubjectConfirmationData InResponseTo="_15f66d2d-628b-4d9b-a99e-089d8da862e1" NotOnOrAfter="2016-07-25T23:25:14.859Z" Recipient="http://localhost:8080/v1/_saml_callback"/></saml2:SubjectConfirmation></saml2:Subject><saml2:Conditions NotBefore="2016-07-25T23:15:14.859Z" NotOnOrAfter="2016-07-25T23:25:14.859Z" xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion"><saml2:AudienceRestriction><saml2:Audience>"123"</saml2:Audience></saml2:AudienceRestriction></saml2:Conditions><saml2:AuthnStatement AuthnInstant="2016-07-25T23:20:14.859Z" SessionIndex="_15f66d2d-628b-4d9b-a99e-089d8da862e1" xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion"><saml2:AuthnContext><saml2:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport</saml2:AuthnContextClassRef></saml2:AuthnContext></saml2:AuthnStatement><saml2:AttributeStatement xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion"><saml2:Attribute Name="username" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified"><saml2:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">russell.haering@scaleft.com</saml2:AttributeValue></saml2:Attribute></saml2:AttributeStatement></saml2:Assertion></saml2p:Response>

================================================
FILE: providertests/testdata/oktaenc_idp_signing_cert.pem
================================================
-----BEGIN CERTIFICATE-----
MIIDrDCCApSgAwIBAgIGAVhBgiX+MA0GCSqGSIb3DQEBBQUAMIGWMQswCQYDVQQGEwJVUzETMBEG
A1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzENMAsGA1UECgwET2t0YTEU
MBIGA1UECwwLU1NPUHJvdmlkZXIxFzAVBgNVBAMMDnNreXBvcnRzeXN0ZW1zMRwwGgYJKoZIhvcN
AQkBFg1pbmZvQG9rdGEuY29tMB4XDTE2MTEwODAxMTUzOFoXDTI2MTEwODAxMTYzOFowgZYxCzAJ
BgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1TYW4gRnJhbmNpc2NvMQ0w
CwYDVQQKDARPa3RhMRQwEgYDVQQLDAtTU09Qcm92aWRlcjEXMBUGA1UEAwwOc2t5cG9ydHN5c3Rl
bXMxHDAaBgkqhkiG9w0BCQEWDWluZm9Ab2t0YS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
ggEKAoIBAQCFRpdl2Wm6UtxSzZp+MCV5ljVm+Rg4dyFYxBz2DzCqGRCE5a47JnrkTSsaQGV2IrId
GejmqpMcB3teziPeQ9IoBA4Grqc0FWUwh79d6vTH7A263XTtkyecq1D9qBwl4oDULcG2IbiQUcMM
JRXgiknzOvHKH1p4TLaxQbmRO2qmHoZG0J0azWe2HgM/tzD9wBosRLu1EGV7ooS9xfS02kum2Qa/
CKydS2nWo/84R+s7hLPmbC3FsDc4U2XWmkFealgh8bZUZHZ6f7xTZkLqq7rvhw4p4pulrYqpnGgU
bAsk/IHLiEzAjfz6movtU4B+CHEt3u/XDH8dW1O0aXpTIauVAgMBAAEwDQYJKoZIhvcNAQEFBQAD
ggEBACZxU65IrSV3hT4/0DDG1P4kplvsfg/YYup8f6mgH38D1rdodiMW59mUBipw13qyempSgbRy
UAukbpi3fpYb0Cob6Flo+FQR8+fpeUTePCIO+yD4mlqElAJR5a2J7ZWhycvAGOqvpaHHK+xrJCDD
Y4c1hIOezAveGDskDS0cDcXknwQwdt2eQxGebRXaDSlvbjFPvpd2Y72x2PBxSoXaamdNRbiSUC0y
APle2k24tMMfcm9A/FDDI8QOikV4vQeIjFjGr+yQf+eqBihaL8iDCojT8xXMU/mbDrQhrSWkqpnF
sjls45t/votmhL8boe2A6Cx9yiAWltPISL7+L1ENa88=
-----END CERTIFICATE-----


================================================
FILE: providertests/testdata/oktaenc_response.b64
================================================
PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48c2FtbDJwOlJlc3BvbnNlIHhtbG5zOnNhbWwycD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnByb3RvY29sIiBEZXN0aW5hdGlvbj0iaHR0cHM6Ly9zYW1sLnRlc3Qubm9wZS9zZXNzaW9uL3Nzby9zYW1sL2Fjcy9za3Y4ZHYyNndoIiBJRD0iaWQxMDM3MzM2OTUzMDUyMDExMDQ1Nzk4NzIxOSIgSW5SZXNwb25zZVRvPSJfNTI2MGZjOTktOWM0MS00MzNiLThhMzEtYjdhM2UzYTRlNzU1IiBJc3N1ZUluc3RhbnQ9IjIwMTctMDktMDZUMjI6MTM6MTAuMTQ0WiIgVmVyc2lvbj0iMi4wIj48c2FtbDI6SXNzdWVyIHhtbG5zOnNhbWwyPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXNzZXJ0aW9uIiBGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpuYW1laWQtZm9ybWF0OmVudGl0eSI+aHR0cDovL3d3dy5va3RhLmNvbS9leGtiYjU5d2IyMFg5Nk5ZMjBoNzwvc2FtbDI6SXNzdWVyPjxkczpTaWduYXR1cmUgeG1sbnM6ZHM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyMiPjxkczpTaWduZWRJbmZvPjxkczpDYW5vbmljYWxpemF0aW9uTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8xMC94bWwtZXhjLWMxNG4jIi8+PGRzOlNpZ25hdHVyZU1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMDQveG1sZHNpZy1tb3JlI3JzYS1zaGEyNTYiLz48ZHM6UmVmZXJlbmNlIFVSST0iI2lkMTAzNzMzNjk1MzA1MjAxMTA0NTc5ODcyMTkiPjxkczpUcmFuc2Zvcm1zPjxkczpUcmFuc2Zvcm0gQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjZW52ZWxvcGVkLXNpZ25hdHVyZSIvPjxkczpUcmFuc2Zvcm0gQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzEwL3htbC1leGMtYzE0biMiLz48L2RzOlRyYW5zZm9ybXM+PGRzOkRpZ2VzdE1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMDQveG1sZW5jI3NoYTI1NiIvPjxkczpEaWdlc3RWYWx1ZT5XZk5kRjRJcGx3aU44eWhLaGgvakNHY0Z6TEU1ODVUTys2MEVUcFp1OG9vPTwvZHM6RGlnZXN0VmFsdWU+PC9kczpSZWZlcmVuY2U+PC9kczpTaWduZWRJbmZvPjxkczpTaWduYXR1cmVWYWx1ZT5JZnNXU1ZmcitKbTZSVFM0MGtBdkJkVHB6cGUzSVR5MjBJT2doMFZ6Vm5aWEFMMU9XZlZCYXN3Nk12SGVwMHQ5R2x4Y25zSHN2eHVVUEIwK3pHMzdkNDNuZHMweks4dGlVcDQ4a2V4ZDBFcnhIelNIMkpwVkw2UDdTQW8vY0dyOXRRamxWMVV5SmJ0NTJnZEttb1JlbXpxVS9wdzVQd3lqSXRNODFyM3pHZUJ1Ym9FeXpkMDBzMCtDaFZyMGhoVkhtK0RPMDc2SnNId1VJRjBkWE1qTHpLTGxLM3hSTnMzd0pBY1h6VytJTnRnMFpvQlBEUWU1SlFDMjJrRUx4SkpyTThWMWQxSkd6MlVUM09yTVIzOHpaQ2ZxVG1JVm9uWExMZ0h4c3dkRHpHcXFiV0xET3VQZUdOcVgzOVAzZGErOEUwbmdJVnVEaHdiM3ZYbW5jZW9YYnc9PTwvZHM6U2lnbmF0dXJlVmFsdWU+PGRzOktleUluZm8+PGRzOlg1MDlEYXRhPjxkczpYNTA5Q2VydGlmaWNhdGU+TUlJRHJEQ0NBcFNnQXdJQkFnSUdBVmhCZ2lYK01BMEdDU3FHU0liM0RRRUJCUVVBTUlHV01Rc3dDUVlEVlFRR0V3SlZVekVUTUJFRwpBMVVFQ0F3S1EyRnNhV1p2Y201cFlURVdNQlFHQTFVRUJ3d05VMkZ1SUVaeVlXNWphWE5qYnpFTk1Bc0dBMVVFQ2d3RVQydDBZVEVVCk1CSUdBMVVFQ3d3TFUxTlBVSEp2ZG1sa1pYSXhGekFWQmdOVkJBTU1Ebk5yZVhCdmNuUnplWE4wWlcxek1Sd3dHZ1lKS29aSWh2Y04KQVFrQkZnMXBibVp2UUc5cmRHRXVZMjl0TUI0WERURTJNVEV3T0RBeE1UVXpPRm9YRFRJMk1URXdPREF4TVRZek9Gb3dnWll4Q3pBSgpCZ05WQkFZVEFsVlRNUk13RVFZRFZRUUlEQXBEWVd4cFptOXlibWxoTVJZd0ZBWURWUVFIREExVFlXNGdSbkpoYm1OcGMyTnZNUTB3CkN3WURWUVFLREFSUGEzUmhNUlF3RWdZRFZRUUxEQXRUVTA5UWNtOTJhV1JsY2pFWE1CVUdBMVVFQXd3T2MydDVjRzl5ZEhONWMzUmwKYlhNeEhEQWFCZ2txaGtpRzl3MEJDUUVXRFdsdVptOUFiMnQwWVM1amIyMHdnZ0VpTUEwR0NTcUdTSWIzRFFFQkFRVUFBNElCRHdBdwpnZ0VLQW9JQkFRQ0ZScGRsMldtNlV0eFN6WnArTUNWNWxqVm0rUmc0ZHlGWXhCejJEekNxR1JDRTVhNDdKbnJrVFNzYVFHVjJJcklkCkdlam1xcE1jQjN0ZXppUGVROUlvQkE0R3JxYzBGV1V3aDc5ZDZ2VEg3QTI2M1hUdGt5ZWNxMUQ5cUJ3bDRvRFVMY0cySWJpUVVjTU0KSlJYZ2lrbnpPdkhLSDFwNFRMYXhRYm1STzJxbUhvWkcwSjBheldlMkhnTS90ekQ5d0Jvc1JMdTFFR1Y3b29TOXhmUzAya3VtMlFhLwpDS3lkUzJuV28vODRSK3M3aExQbWJDM0ZzRGM0VTJYV21rRmVhbGdoOGJaVVpIWjZmN3hUWmtMcXE3cnZodzRwNHB1bHJZcXBuR2dVCmJBc2svSUhMaUV6QWpmejZtb3Z0VTRCK0NIRXQzdS9YREg4ZFcxTzBhWHBUSWF1VkFnTUJBQUV3RFFZSktvWklodmNOQVFFRkJRQUQKZ2dFQkFDWnhVNjVJclNWM2hUNC8wRERHMVA0a3BsdnNmZy9ZWXVwOGY2bWdIMzhEMXJkb2RpTVc1OW1VQmlwdzEzcXllbXBTZ2JSeQpVQXVrYnBpM2ZwWWIwQ29iNkZsbytGUVI4K2ZwZVVUZVBDSU8reUQ0bWxxRWxBSlI1YTJKN1pXaHljdkFHT3F2cGFISEsreHJKQ0REClk0YzFoSU9lekF2ZUdEc2tEUzBjRGNYa253UXdkdDJlUXhHZWJSWGFEU2x2YmpGUHZwZDJZNzJ4MlBCeFNvWGFhbWROUmJpU1VDMHkKQVBsZTJrMjR0TU1mY205QS9GRERJOFFPaWtWNHZRZUlqRmpHcit5UWYrZXFCaWhhTDhpRENvalQ4eFhNVS9tYkRyUWhyU1drcXBuRgpzamxzNDV0L3ZvdG1oTDhib2UyQTZDeDl5aUFXbHRQSVNMNytMMUVOYTg4PTwvZHM6WDUwOUNlcnRpZmljYXRlPjwvZHM6WDUwOURhdGE+PC9kczpLZXlJbmZvPjwvZHM6U2lnbmF0dXJlPjxzYW1sMnA6U3RhdHVzIHhtbG5zOnNhbWwycD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnByb3RvY29sIj48c2FtbDJwOlN0YXR1c0NvZGUgVmFsdWU9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpzdGF0dXM6U3VjY2VzcyIvPjwvc2FtbDJwOlN0YXR1cz48c2FtbDI6RW5jcnlwdGVkQXNzZXJ0aW9uIHhtbG5zOnNhbWwyPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXNzZXJ0aW9uIj48eGVuYzpFbmNyeXB0ZWREYXRhIHhtbG5zOnhlbmM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMDQveG1sZW5jIyIgSWQ9Il9jZmY0OGY1OGE1Mjk2MzJmYThhYzNkZmVhMDgzYjc4OCIgVHlwZT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8wNC94bWxlbmMjRWxlbWVudCI+PHhlbmM6RW5jcnlwdGlvbk1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMDQveG1sZW5jI2FlczI1Ni1jYmMiIHhtbG5zOnhlbmM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMDQveG1sZW5jIyIvPjxkczpLZXlJbmZvIHhtbG5zOmRzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjIj48ZHM6UmV0cmlldmFsTWV0aG9kIFR5cGU9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMDQveG1sZW5jI0VuY3J5cHRlZEtleSIgVVJJPSIjXzMxYjY4NzUyZDJlZGQ0ZDE5NWU5ZDFiZGFiOWQxYTZiIi8+PC9kczpLZXlJbmZvPjx4ZW5jOkNpcGhlckRhdGEgeG1sbnM6eGVuYz0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8wNC94bWxlbmMjIj48eGVuYzpDaXBoZXJWYWx1ZT45M0NsMkYwTDU2aWxHaGRQdXdld1FGNXdnblRGS29jYzR1bEhLdjhmTEtCTWYycE94YjNWQVllcTVHdXV3cUIxNFVnMlFBOGpCSjNRcndMOWc2RkU5WlNFVWs3R3FYMTh2ZUVVc3RMZ1R3aGFrbDg3SGNJSER4WTR2NkJoTTVkQVBZNEpSUFpHZFNTdVlieEN4TGJ6MkhiYUVMSlZwRmlLK2tsTFdNM041T3laWGJQK3NNZGNTM3pITDd2Z21Hd1BGWld1eC9ZV0ZhaEhiZ1pqSE0rM1lvYW1DaTFCNFZxcW9KSW9NdzlnU2RuUU5lbXNMamxDYjRDUnl0QlVzL2k2clB6a1BhQzdITm9sbjRyMS9maWRYdGVDdkhZc2N5ZjA2YzhLcWJXUkE3MFlDQkgvMXJqVTEvYXovQlRnNTJ6R0d1MDF4NE9pSDd4dVZXWVU1UUJHVUQrQzFuc3hZVWJ5TitCN3UrcEUrcGdyOW9hRWNBalhVVUlYYnkrb3pxWDFJOEdyNDdVWjdRUWh6Z1l0UzcrSjl5dTZhR21pa1RVSE4rYmJ3d0hjZXJXNFZnWEpjWnBXMzRuWHFEdGFIUEFwaWxwc1F3QWV5VndMWnVvdVBKbEV0ZU1FbkQwbUN1cGxJMHRtbXJjcG8zdk00VnlTeS9EakZNbm9BSE0zK1hEQVBXMitqNm5jTDlaVU9sR2VmRDhDTy9ONG8rVTJYbVhmaGNvUjZSRHAybmo1T1c4RU5la1BIZEY0cXIyMXVreXJqc25qZngzUlhQSUx6RXJFN3c0MGRtNnRFa2VMYzlLZEs2TlNCc1ZmS2Zna0ZpQWdTK1AzSHJ1MU1rRW1OcHhTaG9qMWJtNDVVZnZqejM2TndzTnZpcFBTRWhtKzN1b0xOcWhoU1MrVjVVeGFXVERQbk5mcExZVGZScVpVTHV2eGo2SmNBS0o0aE5xTlRWZEN1azBPTCswUG9pU1dtNHltU1haQ3I2ZVEzSXpxL2JSNUV1anlBbjZIdXhXN3ZzTGkraG5rNG1BUWhTT2ovUUFVQmFMRWJ3cDFjWUZXWHFjaVpiOVRjRFA4K3NKQTBqejh0WlBBd3lMMERCS1Y0VHJ0ZE5ZNUpjTEVucElpbnFqQ3Ric1NzTzNjUUt4QnE3aFBabnBjWUtnT1V4NHEyZ2huN2tiaGY5Sjl5MGtNcTIvMXN1bWc5K3E1bnNWM3FrNFZ4aUtxa1I1M2x6Tm1yYkZJVTFTOTRjZFYzTldiUVlmK1ZlNkdPU1ZFbmlWcmZDcGZteFI5Yk96aTRGTXpVNEllbWEzNDJ1ZlQ1RTk5NTFrTnJWSnhLQ1c2TmsrTXFNNzJsOW11c2NOcUJUMkpMK2hKbWJBWm9kSHBuUnZsUlp2MkZmcjRCVWNhck41bnQvWVQ3d0ZhVlZWNDdBTnh4SW82YXlsTW9hRjVNc29WSWpFWm5jQlJpTjczNWhMTXVYNFBBdkxXWE93ZW9kVnBXN3k1R0FnbVVyUVN2SDl1QXdBSXBQaWJaS1RQWG92dXpLNEErUDlJV09mazdWYithZ2dHQk9RV3djbXA5dm52OXRJbXg4NjRLOEtGTnFMV0l5RVNmQ3dINFdmelJSbWNCb1pTQ2JsckVLbDhKUFQ2bGM1UkIvRkJ1bjBUSzlhNU5MUDRjSTdjMTZLUHlTeGVGTWNHampWcnlkZHpNRkxFYm16R0IwdEE3TGFjWHdrV0UrSUhvT0d0cUloZC9MSlRWb1o2UUJCcDlheHBVa1dGNDVIdERVWWxzRExqYnpYcFlxUnA1WnNWR2h1cmJ1V1ZydjA5aUxESlRYRlFtc3J2R0hJeW9sU2NkMnRUMmZKMGg0aksydThiZ0lZUmx5SmFia3RtUjNHT0Q3dUZUN1JpZGR1STJ3V1NnbmViR0RuMWRnaDkzNEdmSTdCUktpVnZBMW1heE9IbnltSzRSRTVid2JYeGJadWx6UWZDakZuME9UNEpZUit4dDl0aU1NRC9RVUJCYUdTU2MrSHBrMnRpVjFDc1ZPVDQzZ3FCZmFtOGI3U3JTdTdDOFF3dVZCQmRFcURmeGFsZnpnNUVNOFNBSjhHdDhmUHNCTEpybkc4aFhtNUpWbGovb2M5dnhYaFg2ZFM0YmpEaU9hSng0ZVU4Z1FYQVduYW5CK25lcCtnZzhRemNURG1neTRuZnFKRHhzNi9JVWxBbjcwbWZycmFNZElnV3BHWXYyb1UvdFZsb0cyNmdSVlh6SHBQekRPNHd0UVVkUmE0aUc3ZDZPUnFPTVRrWnlVa3VhRGZ5Y1NVSDZZZUgyK1hyLzVVOS9DU2J5VDNLeHN5djJwckxPdFBLTEVIVloxSU9lUUlTcy8rVXBaWmZEQTFFNWNleFd2cnhtUURtOXZFa2k0bHJYb0NxSXI2YzZvT0lzRzI5MlJ0WHlROGVKMlZBM3phWWRsWEVCbzJpU2RBTFMrUFJteE55aUYyaWdBTzRvZDQ3ZWUxWGJkeU5zOFU0L3l3S3o2cGxPT0ZyRjhaeEdLOGd0ZnRxbW1wdWxWcVpQZnNuYmsxOHUzVitaWjBUaWhkbmJWRCs5YkRxL1htZUh1eUdyY3o2MWhxRm55Zkw1SUtWbW4zWUhqL2J0cXM1bjFWTHZKNHl5QTh4ZUZISVRQUjRkSmxYU3hxQW5QUW5uSG9Cdzk0cnFKUUQwdFZQS3pkWWZiQVlOeEMzMmpRRXB5T1FKSjNrclphNVRHMFJySFp1ckdtbTNHZTlhemwwR0QwWGR5YWxBdVA4Sk8zZEZtcjhHS2h0R2sweTdld01vYVlYUE52NXAzNWtyNVptSWpmb2VhUWlMUlBiMktLcnlqSlhERndPZjY1ekcxQU0xaThWMjVMZHhIUzJXZDQxUHZGQUdYOG1zN2ZGTWJtQW8zNVlZOTY1MTNTWE55cmZTM2duMjNFSzR3S2dNTDlkUkxFWjc2ZlVCb1BlWTg1Z2ZWZWVaWmtBTlRzTXFYWVA2ajB0QllYZU5TSGdUWDI2SW55b09ML01LeFRqU09WL0hEdXpGMnp3V3JDdWowbnNPakIrZzVYTW9uTlY5cm44dkRYZ2lTclAvVzcwVDRnbWlUT1dFV2h2ZTZlS21sZDRLTnRJTHVyWGZVSXR2SHgzbEdwRzVNMEhLNW1hWkxJTmE4NUZQSDgwSzVDY1pYMXR3TE9VQjVubGlPWms2NHllUGNaY2puTFhXNWhvMVZOQUo2TnpKUkxwNHBkODczTzNucFEwMUVqektYWGNqUzQ2SFppTnVySk5Lbk9ZaHliOGhHdFNhYUNRSVVoWlRrSGFRT0c4dnFrd3ZTbGhpcHZCamp1SGFUMytudWdoOXhYVzhta3JwZU1tbU85QlV0L05PU29wSHpSdHQ2QW1GUnJTeDRNWGJoVlplYUR2bzV4U2FPWFA3MUlTSUJKcjNudVY3aEh6REs5Z245R2EzN2NuQXgzMDNvdTJBcmpldllxaHEwdGNHV055ZkpkK0ljRUE5ZDEzY09rQmVvMG8vRnp6NHRkWVpTN2l2MHdHWkRsOVVkR09HQUpmMHMraDNqbFZVeUUwTlQvSC9CSmQ1YWsyemF3cWlCUmU2SzNSbmR2bm1IZXBmaTA3TTZibnVGSGRQM0h1Ny8zRmFjV2VtQllSU0htaHhhM0xUN1lBTjU5MWovVDErS2E3WmY1VE1WbmJoRFVtTWVRTTk5Ui94S1VBOGpaeFVEUnRhckd4YkxrVzBMVytIUHBqb25ZN3BVMThEQ1lhNCtXbWEyZlZWMDNrUmlzaUQrc3RVUUY4MXhXTVVjdWxudk1xeGpnWS81MUh2NS9tR0dyVFpBZTdHTktqazE3bzdnRWJOUFhCcHZFazZFbHA5SVlSVnhJRm5YOVZtSXJPQmIzQ3hrbU9OY2pNUHhic2NPYm5WVXdrU0djemZHWTV2SGg0TXk0QnZxV0RwWkZYb2FCaG9VNVQ4b2pLY2VLbjdJTHFmMUlqaWhGMkhQeG9MQ244QWxCME5VaU9pbjBxMUtzUk9lR08rWGFHUkl6VHdFZGxDTUpPelNETmpEVnhKelZrOHpxK2c1VG0yRGhpYnpQVmtxTlN5RktORlFQVllTMXFMbzcyS3NsZWtVVW9pUTRVVVQvOUF6Z0VDTXVWMUJVYVY4aHduUjJLNnNZQ3RkNDVieit1T0tVVlhUTWROUW55TXQ4Tk9pY2Q2YWtQR0N2LzBid0Q5MTk1STByZWh0eitLSGZUWENYV3J5MGtZNWRuZG1yZjg3NWlVeU1PM1dXaTFTQnpRT0NzU0UxOXVPR241NEEwelh5NUZsbnd6Z2RyeENyWldpQWJkdXhEenlNcU5EVXZaZU9oRGVCaXB3R2k2MmN4MkJtWHRrdEtGdy9jWjhBVnRnS2ZjZUJkSCtkN1dzYU9DN2R2UlprcFNVV0ZkOVlnaGhZNlBrb3N1akdlQjZOR29KbVVuMVY0SGt2UytGTUxoeWw1enRhc3hLMUlMNkp2cWFZbVVEMHI2eTVwU2N5TGVQSGg4c05PZitHVGlKZkowNDh1L2tDZWF0RExqbFA2NkVNdHJQK0tLcGNyajMzd1hGeWJ1a3B2cVNsRFFiZFVnaFFsN0owZStqRjVrR1BlRlAySWt5eHVOMEJNQjZCL3U4cVhCQ0lrMzkrYnkwd0toMG44Qk1pQW5ETURDdWsrSGVJQkdtYlYydGFBZXkzZ3RJSWZ1K0JPU0s3aVBFZzZKM2t1aVJXRlhlTVBrZGxmbm1TaGtYQ0RnZXJqb29aaFIvaE9NQVkxSkIxUDQrQXlkSXR4Z3JzaEQwQU52U3ErNmVrVmZiNHVadnpvR2k4UDRuN3JlaHF0aTE4dlo2RTJXVVAyVFVEQk84T0FSY0VDaHcvalV2NDdTTUs2WXBYR2pFbm05VW1GS3pTNWJ3UzFoOWtTV3lzaE52bWEyK3dmVEd1UlZueS85Z0RISmZnT3owVUNta1JtUklrYmhHOXY3cE9DZjR5N0w2NWZsUlRFbitLbjJZTStBT042TTB2NTlWZVh4WGNjclNXRmdwSXJSaktQcDVlR20vNzJZL28wTmVpQkx6L1VtMTd5TlpvRG5kekJvbVRxMnpSdE5Eb1JOd1NpS2JGdFROaGtnSG05amJPN2lZKzk1eUw4bmo5ZElJcFpOSGR2a0ZkUWRtelQ1WDNmVWEvSjNER2NhRHpJZHlpTWM2cEhCN3Q5R1l4T1dpWnF6ME9aQUYyZWZGcGM0T2d0YkhFRFp5ZldBSXMxeVpuS3ppQTRjNjl6SEJ6TEFnYitQc1pVWGs4SUNXWUIyZU1OV2RETXIrWU54Nml0V1YrUC9PallXMjg0ZUZvMEFyY1JZQVBnZXpiaXUybmltSmJSbnhxQW9jcThvbktkOWtFMjBuTDJYTjdOalRBZkNMNDhncFFadGZOZHBJamFUQ2NnK1pFUk16bGhZRUhna3NUMklwVzR1OE4xYmVRd290WHA3MzdYNzNpeVRYR1gyZWFhTEhCZi9DU1V1V1BRT2dGUDN0cWo5MW9VWC9ucSswNktTbFVuL2ZzcnFQT3hVdi9nOWNsdEx3RE5wUUZlK2R4TXN4WnRtWXVyd1JnQ2ljL2VxWDhRT2c5NEZnYW5UMHpCTW45dGFPNitqcC9NaWg1VDdrNzlNek02R3pHR1RKTkg4Q05qdUhNZElaUkhMeWRSNDhEMDRRRFBBc1J2YVAvVHN4YmFFNkoyWmZEMnNaVTN5UnAvTm4xNHRHL3l1U3g4aE40S29kMkpLWllTSkEzK0NpM3JseHgzZmx2dFlBQjVIakFSWm5YbGZXVCs2a2RLOExDN1B3TXlXK2Zld2V4UWs4MDQ1U001VytSZU45elJoRVBEc041TmZBaUVSeFgzMHNmd0NyOEs5bTBEbXE5b0wySmNqZTdHQjJQc2R6UTV0cTZhTTN4enFjU01uQ0wrSmUvd3hKYXpESDF0THhzZENhM3g5QXlwSXFmU2sxdkxtTHB3RkpBRk5GdEkrVVRuamlnaHA0WG1qQ2VnSlBrN0V3bG95NENSMVZXSkdPYTB0N1M3WTlwYkg0OU1vc3EvdVVFWFB1MXUvcmROZUF1Z24wVmROTDRDMGZhbXZidm1KVHE0K3NVeGhvYVBzVmpLYXJVYzE5TERmWWJIem11QmcwZTZrdGptd2VNMWZwM0JUMkNObFBXS0c3cXdrS1I4TXpvblByTUc4UFRjVnpvMjZHNUtqT3pMRGE2V3Z0UkpwRkxVMzd0TmVxY0FRdDROU1FwVW1uaW5CMHdSaG04T1BjVzdwYno4WG1qNFczekJpdkRVTWd5dmNET0hCWWx1eUlvdFpNL0ZoV0d6SXRlaU1UR1MwOTZFNnBLTEloODk3QklGMVdLaE1UMmF1SmNmZzRLOG1oOE4xem5xR3RQUURFNEprclZjM0V3cWpEcmZXcGczQXpNWUdpOENIWmgwV3E3MkhoWEh3NS9UUVJ2K3o5NTJTMUhNRE13TmpJMUxUL3Fvd1BCTHhkeTI5QlI5OXhHLzdGZnRzRDVFdGJ1ZWVocnFESkxqU1IzMVlGaEpuZmp4VG1pN2s0dXMveWhKcjhQdHRvS0ZKL05nZEdnTnh2d3FJZkw3ZjNPRnBXNWFrdXd3N1VkVEg0MjBhNDM2dkpMczZraEFKWkVUSFE5WURyMHE1Rm5DMHZMbzhZaFoxQWV1ZFJ4L1I2eEYrWkpzZkxOa3pzZHBneTZFcUxOckk2MkU0T3ZEZSszL3B2RDZkSHhnd05DdzRoL2FaVXo4bGI2R0w4V3Z2dFFMSklJanRNVW1UNzdwS3F6V2FaQWYrblFBQ1ZkbzltRjBRbEJ2cGJvWGlUUDYzdmZkd0wveklaOVBmNFpLdS9EYVdLRzFmaDRCL0dmZ08yRzR3dW5vbVpUenl3ZVBzVWdlbHNiUVFCN2Rmc213Q1pLUW5GeFRyYlZ1WnhUckhvaVY0SzE4VExXc2o3RTNWcnJmbWhxWmFkdnM1Nkw3b0cvbHNGS21jN2FuUGwxVVBNYTJzbmphQ0NLUm1oMXZXZkVhVmVBeC8yd2E2elVjUTg2T29qN0ZTdFkwMlN0SElOWFZnMTU0RnZjcWw5OGZhVlRML3BHNGIzRGg0L2lwU2NJZUJlWUc0cVFteFRCZHUzSFlyL3lhTEFObnd0S1pVUHFielJyWmtvbEJZV3lEaDhGSGc4WldYZjRrTThYSStHckRGV0JwRTY3MFVaYkNOaEJGQ2VRSlRxTk8zMnpQYStjMVhFM3E5WjNGRXF6am5lZW5KRkdKTEw5RWd2bFNkUm13cWVsVFpoQTVIeEhxa2loVHhYcitMdGp4dEZjejd6QjYyRUd0RStlUVJvNDlKdXJRR25PcmFKOCtyWWRBS1lRSU5FQ3lmSlY5THFITE5VRWU0Mkw0a0V5d1hEMXlndnYyc0VHeXFuc3VGRGpWZGJKUVQwcDZPR3NKZEhkU1RpRkRucXhXUXQyWXBrVVFSVkFubHR2dHF6bWJzaDlRdVJ6UXpZdjlxNHh3SHp5dW54WWEvYnRxSHhFYVpkOCs1VTBYY20vRVJjZXVWN3AyMFNYdjZKN1JTT2VWOStkWDFHVnYrUGdINXdTaTNTWFZWeDBKQ1RiMDkxVHhRMG8yNFFyNmRjUFo1R2Jtcm1iWXNRTDc4NDZhYm1FU0NwejVTTTBGUWVyRUhseUhjVjZ5QUVVR3loK1pRZnk5c2I3c3kxS0JQaTFHZTQ5dHZSUXZsRUorNC9DVmhBTVNpK1NOVHdqb04yeDl4dDBvR1EzZ3hrMjk1MWp1eC9Ic3JJMm1ySU9vQ2VtNnYwWmViN2dic2xWWUczWG95MGhlbGd0WDloMVFJZHJjdEdZWmFvUGVCVlN0eHo3Q1NhRU9RalkwQ0ovZTNraEZ1MXl3dkQwV05MbGtPV3lLYTdCZzNkdnpPRjZkSkI1WEQ1R3RaeGxieS9rTG9wVDJBRXc5NDl5NEpPNk1lZEVUakVhQ1VSL2VkOVNNNDRLT0VyTC9rYkF5b1dvWWpTVEJtNkl1cHdxWTdJVTFENWtnWkhsT3Ewc213bjJmQ2NSSHZGSklvclN2MmlIc0p5K2pLa1RaZnMzc2hSbUxYRGR2QXhiT0liS0NsODg4di9kcHdnT1lnWkxQWXZCNXQyOVMxVFl0V0U0S0dVakF4UGI4ekJQTjZYWFJRMUNWUjdxZHlqVWp0bjFOZHRpVEZEV0p5dTc1RVRBYUwxWmlMRG9uaDV0a3Bid3h2bXpLZDV3d1J5WEMrWElUa1czbFM4YlUwNW9KN1QwbVhKa1RQWm95Zk0wbXhkcHBBQUVWQTNCTzVKUHdGQmlNSGNqUEdEUEZOSzJoQk1BQTBYVjV1cktQckNVSWpCc3YyTEhpQ2VKdXhhK2ljd2hlZjdkcUVaSDF6OXdYazJZRUc2RmMrdU9Db1VVNTNVT00rbEpGZFo1OVhENTc3MTVRcCtxSUdHak5tM09Gb3ZNdFBUV3NuRVFsdHdjZkhoMFZFaXd2UU1EL1Zxcm5TVTViQmw0dVhDVEJLT0E2SjQ0cWtmU1greW04d1RtV1NOY2pWbjZqNlNER3hvNmNCcEovZU5scktjS0wrVnVUeSs1dm0yTGZpNVlidDdMYnNmR3lqOXhWTnM4b2orbS9QVzE5RFQ1SnNLcXlucGxYY3h4dEdTays1YnJGVlVUdnZ1cFp6RUM0VkhQb1ppMkhGeVcvVmdiTXlZTVlDdzZNNTllbk9nQ1ZmS043aFNPMkordlR2dnlzdU92Q3p1OXVJYXVtVmdYeXhJb09vdml6NUJGcUV5alBvZ244UDV5cUFyYURtVzVYbUZGSFhaS0d4YXJXTS93NkE4TjZ3MS8xRWY5ektqMldma2NtMTlnUWFJa3FqU0dRc0t4cFJ6R0dMVFBJaytYM05CaUhIZnFEaytWdGUzSlBGOENFZzRydUtpOFVyM25zc2c2T1FBVERSdGpVMFVtSkpoNXdET3Bjb0x6NlFqa05PR0RyaDRQdGpvUzN3ME5DNlpPUEtVV2Y3MjFXMVh5dy9CWUdZc205cUtBUk9OWFYvSUtacEdkNjUvKytrMnBYVkpJU2JOd09WUlg1UFNRaENoT0pMTzJ5cklJY0NPN3VzODEvR1hYNkpaY3RiNUVuTThLcFh0ZS9MMXRodDRXZzJrbms0MEwzNEdOMEZTbTNtbXJCY2tMTVRIUGRtdUQyWDE2YWtCMTloZmNvK1Z0WGo1UENEQWJ2UnAxTGNpdmJVR1RKM0JpZS94dXQzMk84OWdST1hxNjN4d1hLekNDSUhnTVZLdnlOMnhlZGRRYkFYaUYwV05VU3pqVDFSMzZTbENoMWNxcnYyOWFQeXJYRFk2M3lERnU3eks1OEhzMmZ6aDczNEs2WlZKQy8vQXNMaEx3MThWQWlqU1NDaFRodmUrVFpKRithRU1XemJPdTlLU0g3NEZ3OGdpbGw0Mkk0NWUvS012Qk1ZS082NEN1STJaVWRGbFFRZWl0LzVXNit1dDcwN0x5ajR3MklGNjdBSWZWSDg4WnNvdEliYm55cUpGa2xnZXJkeXhpVmpQcTdacVNXQjV1MHZzZ3pZMkRVRnhzTHloc3IrdDFUdnNBaFJTVVVrSnpFTlhwdDVoVmlZSVVVM29zRFdja0FSQnhLNUNETGoxWmdGQUZ0enJvbms1QlNPZkpwWDY1ZldhSGpHQTdQeUxFYXVoL2JjNCt4aFdNTmNhbnh3d2JYQVVDM0gxdTFKWndPUUVHK1BRTW5naGllOCsxWXdvZXRqTmNZQjVJeFh2ZVBORTFQcFJtVGZOUnpyeFdWd0Fjc3d5cHFaRnc0VXlwelgvZnVRakVPMS9zeFl4NDk0ZGJsa2kxVmFOTW8zeTJpVzR6czFCb3dQekRVTWttYzlxdnB0SU90eHVCYXB5eEMwYnhidFVabkdsQkt4UFlRL1BWd1NNdkgwbWJ6clJKVCtUVkRyV2pQNzNWRU9paE02QU5TU213ay9KTWMwZExrUDU4Z0tXSlk0VVppa0hwWCtPZy9kdkFpci9xTkxTU2dmZUFRYkhOZE1XWTdJdzVUUW1RdytFd2VvdFE3ZUFnZVZqd1pZampQMTE0TzRLUVdPbFBlbEEyQTwveGVuYzpDaXBoZXJWYWx1ZT48L3hlbmM6Q2lwaGVyRGF0YT48L3hlbmM6RW5jcnlwdGVkRGF0YT48eGVuYzpFbmNyeXB0ZWRLZXkgeG1sbnM6eGVuYz0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8wNC94bWxlbmMjIiBJZD0iXzMxYjY4NzUyZDJlZGQ0ZDE5NWU5ZDFiZGFiOWQxYTZiIj48eGVuYzpFbmNyeXB0aW9uTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8wNC94bWxlbmMjcnNhLW9hZXAtbWdmMXAiIHhtbG5zOnhlbmM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMDQveG1sZW5jIyI+PGRzOkRpZ2VzdE1ldGhvZCB4bWxuczpkcz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnIyIgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjc2hhMSIvPjwveGVuYzpFbmNyeXB0aW9uTWV0aG9kPjxkczpLZXlJbmZvIHhtbG5zOmRzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjIj48ZHM6WDUwOURhdGE+PGRzOlg1MDlDZXJ0aWZpY2F0ZT5NSUlEUGpDQ0FpYWdBd0lCQWdJSkFPdnBadEpOdFEvck1BMEdDU3FHU0liM0RRRUJDd1VBTUZVeEN6QUpCZ05WQkFZVEFsVlRNUk13CkVRWURWUVFJRXdwRFlXeHBabTl5Ym1saE1SZ3dGZ1lEVlFRS0V3OVRRVTFNSUZSbGMzUWdUbTl3WlM0eEZ6QVZCZ05WQkFNVERuTmgKYld3dWRHVnpkQzV1YjNCbE1DQVhEVEUzTURneE5EQXhORGcxTjFvWUR6SXhNVFl3TXpBNE1ERTBPRFUzV2pCVk1Rc3dDUVlEVlFRRwpFd0pWVXpFVE1CRUdBMVVFQ0JNS1EyRnNhV1p2Y201cFlURVlNQllHQTFVRUNoTVBVMEZOVENCVVpYTjBJRTV2Y0dVdU1SY3dGUVlEClZRUURFdzV6WVcxc0xuUmxjM1F1Ym05d1pUQ0NBU0l3RFFZSktvWklodmNOQVFFQkJRQURnZ0VQQURDQ0FRb0NnZ0VCQUtmVWxjaXcKRFBDSjlKWHkxTnN4S2ZxeU9mTFFYRktLR2dOS0tSTHpMQVdPNDNHZ1FZclY2N1FhQW55NUx2Z09hODFLUU9YNEl2akp5MG5kL1ZyTwo2akc5cFRkTVI5dkZIVDBUdTNXMVFVc3lQUFNRcXBLWHdiMlpDU1hOZ2hZRDVnQnJOdG1PeEFPczFBeng1cW1JekJKNEYwUmtSaXZjCmRiWFNmQlVCTHBDbjFNMjg1YzR0azVTRXZvUmdCZWlzTEpKREI3aG15bEExVjYvK0JOK042YjNNK1paRy8vQ1R2T2pKS2dud2tNQUwKelhiUGJRV2Ewa3BiTUxETXo4S3IycE9ETEtiM3JHbWRmNFA2QTlhQTQxazZMNGNsVHNOZGNLN1p3SjJmR0Y3OVdHekU2Ukx5QlgyVQpTM2dQSFVYdC9XTVFaWHlVTE8zZXhpVERzUVhXRmY4Q0F3RUFBYU1QTUEwd0N3WURWUjBQQkFRREFnUVFNQTBHQ1NxR1NJYjNEUUVCCkN3VUFBNElCQVFCM2ZRWnoycU5Bby93b2hQTmFSMnVBYWtMdDI1Y0tyc2JnbjNYd08rWCtEUkg0bnFNU1prSGRMR3I4MnhEM2kzM0IKTFNRd1h6RmVjRU4rQ0E4U2xwTm9JWEh3azZENlYzdUVCU2pLZW1adEpGQ0pxZThkKzVxdDNHVjUwQUttdEV0Mk5jN0I5NFROOHhCNQpFbVpsYWRuVEpXY0JnU3V1ejFoTFhRUTAyZEozZERxOEZNbHlSNzBTMkRUdHVQdTBpMi9mNkt0cmxtbU5zVTI0QXErZkN6RUc0M05xClNoT01lTTVyRHhMc1NKQ3RqZk1Sd0c4WWV5TmY2Y0NNU014WUFKZnJUeWorQVM4UnRaUU5HRjd0WCtleEVXSE9mMUM5Q0J6SHRLUkMKbEJSTDZ6RGpscFBDSUR3Y01GaThOWHZhaVZjbHhJcTNYZGZxeGlHQkJFbGRraGdaPC9kczpYNTA5Q2VydGlmaWNhdGU+PC9kczpYNTA5RGF0YT48L2RzOktleUluZm8+PHhlbmM6Q2lwaGVyRGF0YSB4bWxuczp4ZW5jPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGVuYyMiPjx4ZW5jOkNpcGhlclZhbHVlPmhuQ0NyWEFCK2JIdEEvNHBKeVBFWWcxQ0RwQWRGMXl5clY0dDdaVGtTRCtCSmlRY1B6SWIyVTZFOC9UeUNsQ1o4OXlsYjE3SG1xNjlzTVFqZHMzWUllVHRCdjVxc0tBczFHUTRvQnBDU1F4UTZEa3JlL3FZdVgwUU5vaUdNMDR5dUI3OTMzVFdGQjIxbHFUa0NrNDJVd25GVlRjTHhLWHhrM1Q3cmhUTWZJbkxheVFWRCtKU1ZWQ3F4TFM1L1R0YWhLWFBlb1FsQ0VSUi82ejRMV1ErTEhpU2k5dXJNTEpLZk5MdDhBNmxpRmhCbmM5KzNtaVp3dGgzR1lEVWRGK2c2VEk0d2lOQzdlNHdvbk93RmNnU211amQwUzVtbkxidXB5dVB0NFJDVGYrTC91ZE1rUXBjVWc1dmZVTndnK0dmdUhDL2NmNDM2cWJmMEpyYytLRDNGUT09PC94ZW5jOkNpcGhlclZhbHVlPjwveGVuYzpDaXBoZXJEYXRhPjx4ZW5jOlJlZmVyZW5jZUxpc3Q+PHhlbmM6RGF0YVJlZmVyZW5jZSBVUkk9IiNfY2ZmNDhmNThhNTI5NjMyZmE4YWMzZGZlYTA4M2I3ODgiLz48L3hlbmM6UmVmZXJlbmNlTGlzdD48L3hlbmM6RW5jcnlwdGVkS2V5Pjwvc2FtbDI6RW5jcnlwdGVkQXNzZXJ0aW9uPjwvc2FtbDJwOlJlc3BvbnNlPg==


================================================
FILE: providertests/testdata/oktaenc_sp_encryption_cert.pem
================================================
-----BEGIN CERTIFICATE-----
MIIDPjCCAiagAwIBAgIJAOvpZtJNtQ/rMA0GCSqGSIb3DQEBCwUAMFUxCzAJBgNV
BAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRgwFgYDVQQKEw9TQU1MIFRlc3Qg
Tm9wZS4xFzAVBgNVBAMTDnNhbWwudGVzdC5ub3BlMCAXDTE3MDgxNDAxNDg1N1oY
DzIxMTYwMzA4MDE0ODU3WjBVMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZv
cm5pYTEYMBYGA1UEChMPU0FNTCBUZXN0IE5vcGUuMRcwFQYDVQQDEw5zYW1sLnRl
c3Qubm9wZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKfUlciwDPCJ
9JXy1NsxKfqyOfLQXFKKGgNKKRLzLAWO43GgQYrV67QaAny5LvgOa81KQOX4IvjJ
y0nd/VrO6jG9pTdMR9vFHT0Tu3W1QUsyPPSQqpKXwb2ZCSXNghYD5gBrNtmOxAOs
1Azx5qmIzBJ4F0RkRivcdbXSfBUBLpCn1M285c4tk5SEvoRgBeisLJJDB7hmylA1
V6/+BN+N6b3M+ZZG//CTvOjJKgnwkMALzXbPbQWa0kpbMLDMz8Kr2pODLKb3rGmd
f4P6A9aA41k6L4clTsNdcK7ZwJ2fGF79WGzE6RLyBX2US3gPHUXt/WMQZXyULO3e
xiTDsQXWFf8CAwEAAaMPMA0wCwYDVR0PBAQDAgQQMA0GCSqGSIb3DQEBCwUAA4IB
AQB3fQZz2qNAo/wohPNaR2uAakLt25cKrsbgn3XwO+X+DRH4nqMSZkHdLGr82xD3
i33BLSQwXzFecEN+CA8SlpNoIXHwk6D6V3uEBSjKemZtJFCJqe8d+5qt3GV50AKm
tEt2Nc7B94TN8xB5EmZladnTJWcBgSuuz1hLXQQ02dJ3dDq8FMlyR70S2DTtuPu0
i2/f6KtrlmmNsU24Aq+fCzEG43NqShOMeM5rDxLsSJCtjfMRwG8YeyNf6cCMSMxY
AJfrTyj+AS8RtZQNGF7tX+exEWHOf1C9CBzHtKRClBRL6zDjlpPCIDwcMFi8NXva
iVclxIq3XdfqxiGBBEldkhgZ
-----END CERTIFICATE-----


================================================
FILE: providertests/testdata/oktaenc_sp_encryption_key.pem
================================================
-----BEGIN PRIVATE KEY-----
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCn1JXIsAzwifSV
8tTbMSn6sjny0FxSihoDSikS8ywFjuNxoEGK1eu0GgJ8uS74DmvNSkDl+CL4yctJ
3f1azuoxvaU3TEfbxR09E7t1tUFLMjz0kKqSl8G9mQklzYIWA+YAazbZjsQDrNQM
8eapiMwSeBdEZEYr3HW10nwVAS6Qp9TNvOXOLZOUhL6EYAXorCySQwe4ZspQNVev
/gTfjem9zPmWRv/wk7zoySoJ8JDAC812z20FmtJKWzCwzM/Cq9qTgyym96xpnX+D
+gPWgONZOi+HJU7DXXCu2cCdnxhe/VhsxOkS8gV9lEt4Dx1F7f1jEGV8lCzt3sYk
w7EF1hX/AgMBAAECggEATD8qZWiGGGm0csNFrOdjGQALj6LVrrILK19v1aogX84Q
yrhxzm9ti/8dDijbi9qRY0zOuyvGR5z1/HMb/Ttv7nUoMD05kEC3Dd5/FfTo2w7R
3pIyzDKh1E9IdqIs7W+L+Li8zI4twzWriM4FaxQ5ElL4zxt3PcoEuDwOH0TQMOsy
P2gLEhzzkFp7TRoKzR87sM6lXoERsGKh7XNWjjS15/w+XHMaDverOOALwWDbv32N
UCdVSAGBx4Ic4mZuJv+e3MH2SAja0J6NLMzCMtrzmfcgVECykMnELi1XftZYbgD+
KulSvHuamygS4KJppnAdly8950C8CxI7fp9QJV/8UQKBgQDUaT68z9e203L/XATr
sa9CgZodWvjrLH7a2e5EvUPq8slmg2VqEYNjHzP4rCoOcLul6TwrlNInxqTIkIPR
mRxBSrG24azfGRrOs34IMTXgqfMVVxbcSVK8OijHs94NTwYrRjMXeaAOnKjPH484
DALrEoEGcrjXmDDKBV9rb+a5XQKBgQDKRVkUvCoILjsA3jG6qaKBwKSZBFla2o6x
YxgOZdeSG2NEuMt74yRHjgV6iogpvw9i/FSY9TAaZsoX0L3I5LWRtwEi3iXtEC9X
qFlS9EPr9encpdd2vu1O6aWXW4IlUWbu3ecdb5UAxu44ObUQS6HiXEfcK8vufF5l
WyilXNerCwKBgAMYYYSo1rDgVe2orp2soiwq7U7+M2Bh7ptLz7AKvgGfdhXbVk/8
X1jDbKN0BszdEa7yPiE4cvOHboFJsdINFB9TclGC6lz55a0banu+E9XpRBSMh1XA
b8c0ZcG7OkIhY6qfxEDUUePEnqUeBZ1Ad45rX5vzcgQaQKPA/kn3gHo1AoGAU/05
wSdk2KLBs1mQEq9PoEsqzbMFm3k3WScJnSjiu3ON48JMFDQ8I04wrjKJ2bPRIswe
IKlnAP9/mSX/OETntArmeOD/pJFc5ywrcwukjH0/T3S5pEAhb+wK629++hn0AOm5
hvqHR5xS3JvxuEN/g+vHlv/EnVGdLL8OgKOtlgMCgYBU/5WF/7dr89uYzjiBvedh
HQCgwd+8aSs+raY5eTzJrJgqVbEMoxhco1GWzWYUjDVw88Y63k+fqIf1DQJFkWFi
9T1nppVDeofoLDLTrh/2oTAHIM2QL6kWwoHiD7b/scF5z/rC21KqobqroAh2R0CP
J2waRoJgqT+P15Lq3PEcSA==
-----END PRIVATE KEY-----


================================================
FILE: providertests/testdata/oktaenc_sp_signing_cert.pem
================================================
-----BEGIN CERTIFICATE-----
MIIDPjCCAiagAwIBAgIJAKHPtM2MqZxXMA0GCSqGSIb3DQEBCwUAMFUxCzAJBgNV
BAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRgwFgYDVQQKEw9TQU1MIFRlc3Qg
Tm9wZS4xFzAVBgNVBAMTDnNhbWwudGVzdC5ub3BlMCAXDTE3MDgwODE3NDIyN1oY
DzIxMTcwNzE1MTc0MjI3WjBVMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZv
cm5pYTEYMBYGA1UEChMPU0FNTCBUZXN0IE5vcGUuMRcwFQYDVQQDEw5zYW1sLnRl
c3Qubm9wZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALWw8EP/dSG0
yy7cL2sQvcRqiTrsVCWzQ5SGgZNGbFJXq36pshZ21H7gFPI/K2n6m4I3vNMCQtRJ
JwfjyrPbuXpVaTMcmg8J3PGUVzaab+dZ28EQ7V9HEnzcVbEgu87xY/SB6SCiJbdI
COZWlsgLUkgZS9CFkw5q6i29nL6nSZw/MmNr1hjJWvVvs1KwnGjB2aF6wYBQKKsY
GxfZTJqKKBGXwIB5qnaWpVQgP6+H/84YCHLUEKk1hgh9AnR1xNVE6e64clcAE+O/
HWDS6QW658TJsw18TapzVbiUGou4TlxJZqEuJv3BfPX3EMsnp0uRurMtwgyzI/ZP
CCHxSMJs4xECAwEAAaMPMA0wCwYDVR0PBAQDAgeAMA0GCSqGSIb3DQEBCwUAA4IB
AQBRwSaE1AnhZU/GzMngLSG6WsOp1NksoEHZsJoIKxUJWdMj9s0Pb/QSGg/W/2UU
CREt6xBd9rh3jv17AwH6+IAvq/zQn36UtOE0/0v85Tp/I6t8v96cTjkEgDHBWSqw
qfPvnrokKUpDRv1cxCL0qh4oW7selKkI0VpZOgpKm87oi6POQJ/KOMfcewlH+DXt
zN3Es8Xp8BAfRRIEk7i3PqZSrhWUguQgCOAOvkb0tGuPDsFLLJJBML7B7aYrS1Kw
EyuowZiDCDSk49KpGOmKYv2ocIG65gr7+am/l2NN+6cxuCf/o9H8UL8qm0LUJdgh
5sVa0DJzIcuOjWsp2QxqTjr/
-----END CERTIFICATE-----


================================================
FILE: providertests/testdata/oktaenc_sp_signing_key.pem
================================================
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC1sPBD/3UhtMsu
3C9rEL3Eaok67FQls0OUhoGTRmxSV6t+qbIWdtR+4BTyPytp+puCN7zTAkLUSScH
48qz27l6VWkzHJoPCdzxlFc2mm/nWdvBEO1fRxJ83FWxILvO8WP0gekgoiW3SAjm
VpbIC1JIGUvQhZMOauotvZy+p0mcPzJja9YYyVr1b7NSsJxowdmhesGAUCirGBsX
2UyaiigRl8CAeap2lqVUID+vh//OGAhy1BCpNYYIfQJ0dcTVROnuuHJXABPjvx1g
0ukFuufEybMNfE2qc1W4lBqLuE5cSWahLib9wXz19xDLJ6dLkbqzLcIMsyP2Twgh
8UjCbOMRAgMBAAECggEAA/py2FpQm0NVK5qAo0yDizh19sdT9FtfgHINhkcFeiwX
Qje/AYTqlDid3MwLTE/Dde94f/pNsk5Dv4V6W8PU5NvX6NmYefnST6va2QQ6rQsn
p6uSNwLcYcWFTtKTqi1Y7fKmQyOpxcCbCUgeFxVAYPnE0jUKikdHB2dedkI9d2FV
ZMSBAlNp5NQQTTojjdvfa3yxFqB/5Gh+tZZnnXm0BmtSHjWvXnpi62Nz8LHtKwtO
qzS6650vhbC8GyuILeBkEa0Zvw2aUhRI0grCjWsbXJT66FrV+pIElOAQvyHP6hlX
zC43ODdXSiFogVkF07umlqmopv4gDLgst8fjY61VfQKBgQDpVytmVrNXI9A2uDv/
0RhCZpmBMgz8o3OQY0KRtZqupeVUGCTKk0zNqxsvUg31fa+DLYu+fYUDRVy58ST7
vTiXjKYllWY5LAacsqtA/1E4BghOuYfU12tGJcOUM1TZNkQ/s2to/fDL0HTjYX6T
STqxoyg9zCnSYpF+u7gj91QuywKBgQDHVcUGiL69Tws3J6riKvFBtqrqcJaRu5OS
t/U+kqM1m5/GOaBpZyropeusQgb/nZHIE/lEur7CHYcTi8PKF2lGxnqWOn31LVrc
rM11r2pweDpCBBQqytNoE9vUzgfueXbPPgl+l1pTDA02u6EE45IHC1bPPbjDx29/
xhfAykD+EwKBgQCrVA3Rvqc/vFQNWYj0dJrTZnIfophWh806nycpxaYlxjl0XaXl
9/HUsWXsgNsu/XH5Z1/MJm2b0TocbSsyi+UZoXP89I1yPRk9xRJpTw5bjFJdO2y4
otNpViEcl/Qc6PODskGTfaFohU7OBa/ihUpLU/kgJuAuUaFy5HJ4NgnhsQKBgQCm
q/K4SfKiu8r27TYXMizT83bp3AYqCJuWHDb1gQryshNgXaLhPyBDsyhhuju70/an
Qvw0GamTCbl+AGlPO1ANm7u4jWPwc2H8DoklE+196SldNaffOASYi7JON4+uOOW8
13R84vTlZ7hMwYobWmUperO1STiKiuGSBwKpcvPw/QKBgBgqByvETHwewxnYoIsp
yn3Fz1KM7MMnRil2sXVTdpS6lpF+iGs7iXc8sBnWKxFFQWnvvx6VvhXw6lCCnEQX
3LH177jH+s0jVQgD+YZLnYDBF347tU7W/dPz5Df7hgmuQhOU/UV9ihm4udutRFbR
4fd3j6wIcv2MZ0iDUVJ0UFYr
-----END PRIVATE KEY-----


================================================
FILE: providertests/testdata/onelogin/idp.signing.cert
================================================
-----BEGIN CERTIFICATE-----
MIIDPjCCAiagAwIBAgIJAMQeWnDJVaTyMA0GCSqGSIb3DQEBCwUAMFUxCzAJBgNV
BAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRgwFgYDVQQKEw9TQU1MIFRlc3Qg
Tm9wZS4xFzAVBgNVBAMTDnNhbWwudGVzdC5ub3BlMCAXDTE3MDgwODE3NDIyN1oY
DzIxMTcwNzE1MTc0MjI3WjBVMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZv
cm5pYTEYMBYGA1UEChMPU0FNTCBUZXN0IE5vcGUuMRcwFQYDVQQDEw5zYW1sLnRl
c3Qubm9wZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOwVVXE67dWF
WQGnFyWLQyaAqco+hargHNq5DWTiQM5EG1GELt5QqW7XQJwgwfIHic7zMBDU13F6
IUrh310gNm0Nygg7DWEREBNZvTdjg999pbRyt5IgeihKh5NEwpH9TGXnbow8QjI7
9AqsB9Irn0fEuLKb1KOF0EMt5XqJnW2oqJrvTdYeGoi7Ckpg9J986XMqLMp48Gd9
/ATo83GHsO7w+NKs0mAxB/Tffh6hrbILtMVMENQB6lv3dIrdmQqPGJ120xO6dmHq
LTj+tNSjhkznSKXBDHT/7vGt3Ra38P6mGCKrDAloAz2XdXJcdiY57PxeQvc0zwRt
JtOxphA5WHMCAwEAAaMPMA0wCwYDVR0PBAQDAgeAMA0GCSqGSIb3DQEBCwUAA4IB
AQAQ2P9aUBaM1kC0XJF86A4aYkqRVuyspJT6+DOYA/pcifZ3zhpXPe2+TY2gKsGk
qfYma7ch6dq4zVErFMwkn8qM7PD1knK/P8SZuUPC3LTH3ETH+beb1lM9NICOItkh
u7RIwwwwhAZKP52/Ww0F59PCv1SPZJ1NkF8XWak4Df8l4un4xsX1jcFkFlUxCDh6
simdFOK3ibMHUZxyGbvK/MwJfKQmMcjo/fIaukv1/2tKXnilSDhQS1161yiX7ooF
dD+y55KaGF5O50K/9DW30E9ZgrSDltD/6aGt17VrX5wnkoUb68CqYfUrRjhpbPYI
LGu96alI6+eLeIwZKpKSbMj4
-----END CERTIFICATE-----


================================================
FILE: providertests/testdata/onelogin/olgn09_response_01.b64
================================================
PD94bWwgdmVyc2lvbj0iMS4wIj8+CjxzYW1sMnA6UmVzcG9uc2UgeG1sbnM6c2FtbDJwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wiIERlc3RpbmF0aW9uPSJodHRwczovL3NhbWwuc3Aubm9wZS9zZXNzaW9uL3Nzby9zYW1sL2Fjcy9ycTVqd2t2Yjh6IiBJRD0icGZ4M2IwNWQxZmUtM2I1ZC1iYTAyLWZiZjctMmM1MjVjNzNlZjU0IiBJblJlc3BvbnNlVG89ImlkLTUzMWIyYmYwMmFlMGE1ODhjMzAyMzQxNjNhZGJmZDlkNmJiZjA3NDciIElzc3VlSW5zdGFudD0iMjAxNy0wOC0zMFQyMzoxNDo0MS4zNzlaIiBWZXJzaW9uPSIyLjAiPgogICAgPHNhbWwyOklzc3VlciB4bWxuczpzYW1sMj0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFzc2VydGlvbiIgRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6bmFtZWlkLWZvcm1hdDplbnRpdHkiPmh0dHBzOi8vc2FtbC5pZHAubm9wZS9oOWdranp2YjNlPC9zYW1sMjpJc3N1ZXI+PGRzOlNpZ25hdHVyZSB4bWxuczpkcz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnIyI+CiAgPGRzOlNpZ25lZEluZm8+PGRzOkNhbm9uaWNhbGl6YXRpb25NZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzEwL3htbC1leGMtYzE0biMiLz4KICAgIDxkczpTaWduYXR1cmVNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjcnNhLXNoYTEiLz4KICA8ZHM6UmVmZXJlbmNlIFVSST0iI3BmeDNiMDVkMWZlLTNiNWQtYmEwMi1mYmY3LTJjNTI1YzczZWY1NCI+PGRzOlRyYW5zZm9ybXM+PGRzOlRyYW5zZm9ybSBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyNlbnZlbG9wZWQtc2lnbmF0dXJlIi8+PGRzOlRyYW5zZm9ybSBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMTAveG1sLWV4Yy1jMTRuIyIvPjwvZHM6VHJhbnNmb3Jtcz48ZHM6RGlnZXN0TWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnI3NoYTEiLz48ZHM6RGlnZXN0VmFsdWU+WFJ3VnY5QW9HL3FTcVVEdTVjMks5bEh3YndNPTwvZHM6RGlnZXN0VmFsdWU+PC9kczpSZWZlcmVuY2U+PC9kczpTaWduZWRJbmZvPjxkczpTaWduYXR1cmVWYWx1ZT5PYXhyZmsyYzVoU3BsbVdvbGpwT2FGbG5RUVhTYW1CY2t0Tk1qY2VwUDVXWWx2WDJ5Ymw3YWEwZXc4MWV0dGp0VXlFMHltMmFudFd0endKSTRhckw1ZEdmSGZQUFFuOVhKL0N2Wkc3WUNpV1BUa2ZjNGp3cXNtOU9TM2thZnYvQTBJb0xPc2ZmQTNyRmV6dThmdUNMaXhMOTJLTmxOaFJOL2JyeUY5YTdGejdBczIwa09tcDgybUJNR3V2TC9BUEZPc3N4U2hteGIwTlJ2Q01KQ0prUkFwODM3Rkt4K3JuOFJGL1BiaUZWWmluUmZPMXFnTUVzVi9lbW5uZjZhd1l6ZDZFUHFTWVg5RlE1anNLWm42NG5YWFd5RFY5WXVBdER5bG8ya2JPL1hXRFFjVkIwOE1TZlA5WDJSV1ZURi8yclkybWMzbEUwSUVwdHIxbkw0NU1aM1E9PTwvZHM6U2lnbmF0dXJlVmFsdWU+CjxkczpLZXlJbmZvPjxkczpYNTA5RGF0YT48ZHM6WDUwOUNlcnRpZmljYXRlPk1JSURQakNDQWlhZ0F3SUJBZ0lKQU1RZVduREpWYVR5TUEwR0NTcUdTSWIzRFFFQkN3VUFNRlV4Q3pBSkJnTlZCQVlUQWxWVE1STXdFUVlEVlFRSUV3cERZV3hwWm05eWJtbGhNUmd3RmdZRFZRUUtFdzlUUVUxTUlGUmxjM1FnVG05d1pTNHhGekFWQmdOVkJBTVREbk5oYld3dWRHVnpkQzV1YjNCbE1DQVhEVEUzTURnd09ERTNOREl5TjFvWUR6SXhNVGN3TnpFMU1UYzBNakkzV2pCVk1Rc3dDUVlEVlFRR0V3SlZVekVUTUJFR0ExVUVDQk1LUTJGc2FXWnZjbTVwWVRFWU1CWUdBMVVFQ2hNUFUwRk5UQ0JVWlhOMElFNXZjR1V1TVJjd0ZRWURWUVFERXc1ellXMXNMblJsYzNRdWJtOXdaVENDQVNJd0RRWUpLb1pJaHZjTkFRRUJCUUFEZ2dFUEFEQ0NBUW9DZ2dFQkFPd1ZWWEU2N2RXRldRR25GeVdMUXlhQXFjbytoYXJnSE5xNURXVGlRTTVFRzFHRUx0NVFxVzdYUUp3Z3dmSUhpYzd6TUJEVTEzRjZJVXJoMzEwZ05tME55Z2c3RFdFUkVCTlp2VGRqZzk5OXBiUnl0NUlnZWloS2g1TkV3cEg5VEdYbmJvdzhRakk3OUFxc0I5SXJuMGZFdUxLYjFLT0YwRU10NVhxSm5XMm9xSnJ2VGRZZUdvaTdDa3BnOUo5ODZYTXFMTXA0OEdkOS9BVG84M0dIc083dytOS3MwbUF4Qi9UZmZoNmhyYklMdE1WTUVOUUI2bHYzZElyZG1RcVBHSjEyMHhPNmRtSHFMVGordE5Tamhrem5TS1hCREhULzd2R3QzUmEzOFA2bUdDS3JEQWxvQXoyWGRYSmNkaVk1N1B4ZVF2YzB6d1J0SnRPeHBoQTVXSE1DQXdFQUFhTVBNQTB3Q3dZRFZSMFBCQVFEQWdlQU1BMEdDU3FHU0liM0RRRUJDd1VBQTRJQkFRQVEyUDlhVUJhTTFrQzBYSkY4NkE0YVlrcVJWdXlzcEpUNitET1lBL3BjaWZaM3pocFhQZTIrVFkyZ0tzR2txZlltYTdjaDZkcTR6VkVyRk13a244cU03UEQxa25LL1A4U1p1VVBDM0xUSDNFVEgrYmViMWxNOU5JQ09JdGtodTdSSXd3d3doQVpLUDUyL1d3MEY1OVBDdjFTUFpKMU5rRjhYV2FrNERmOGw0dW40eHNYMWpjRmtGbFV4Q0RoNnNpbWRGT0szaWJNSFVaeHlHYnZLL013SmZLUW1NY2pvL2ZJYXVrdjEvMnRLWG5pbFNEaFFTMTE2MXlpWDdvb0ZkRCt5NTVLYUdGNU81MEsvOURXMzBFOVpnclNEbHRELzZhR3QxN1ZyWDV3bmtvVWI2OENxWWZVclJqaHBiUFlJTEd1OTZhbEk2K2VMZUl3WktwS1NiTWo0PC9kczpYNTA5Q2VydGlmaWNhdGU+PC9kczpYNTA5RGF0YT48L2RzOktleUluZm8+PC9kczpTaWduYXR1cmU+CiAgICA8c2FtbDJwOlN0YXR1cyB4bWxuczpzYW1sMnA9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpwcm90b2NvbCI+CiAgICAgICAgPHNhbWwycDpTdGF0dXNDb2RlIFZhbHVlPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6c3RhdHVzOlN1Y2Nlc3MiLz4KICAgIDwvc2FtbDJwOlN0YXR1cz4KICAgIDxzYW1sMjpBc3NlcnRpb24geG1sbnM6c2FtbDI9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iIHhtbG5zOnhzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYSIgSUQ9ImlkNzA5MzgyOTUzNzU0Njc2NzEzMTgzODc3MjAiIElzc3VlSW5zdGFudD0iMjAxNy0wOC0zMFQyMzoxNDo0MS4zNzlaIiBWZXJzaW9uPSIyLjAiPgogICAgICAgIDxzYW1sMjpJc3N1ZXIgeG1sbnM6c2FtbDI9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iIEZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOm5hbWVpZC1mb3JtYXQ6ZW50aXR5Ij5odHRwczovL3NhbWwuaWRwLm5vcGUvaDlna2p6dmIzZTwvc2FtbDI6SXNzdWVyPgogICAgICAgIDxzYW1sMjpTdWJqZWN0IHhtbG5zOnNhbWwyPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXNzZXJ0aW9uIj4KICAgICAgICAgICAgPHNhbWwyOk5hbWVJRCBGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjEuMTpuYW1laWQtZm9ybWF0OmVtYWlsQWRkcmVzcyI+dXNlckBzYW1sLnNwLm5vcGU8L3NhbWwyOk5hbWVJRD4KICAgICAgICAgICAgPHNhbWwyOlN1YmplY3RDb25maXJtYXRpb24gTWV0aG9kPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6Y206YmVhcmVyIj4KICAgICAgICAgICAgICAgIDxzYW1sMjpTdWJqZWN0Q29uZmlybWF0aW9uRGF0YSBJblJlc3BvbnNlVG89ImlkLTUzMWIyYmYwMmFlMGE1ODhjMzAyMzQxNjNhZGJmZDlkNmJiZjA3NDciIE5vdE9uT3JBZnRlcj0iMjAxNy0wOC0zMFQyMzoxOTo0MS4zNzlaIiBSZWNpcGllbnQ9Imh0dHBzOi8vc2FtbC5zcC5ub3BlL3Nlc3Npb24vc3NvL3NhbWwvYWNzL3JxNWp3a3ZiOHoiLz4KICAgICAgICAgICAgPC9zYW1sMjpTdWJqZWN0Q29uZmlybWF0aW9uPgogICAgICAgIDwvc2FtbDI6U3ViamVjdD4KICAgICAgICA8c2FtbDI6Q29uZGl0aW9ucyB4bWxuczpzYW1sMj0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFzc2VydGlvbiIgTm90QmVmb3JlPSIyMDE3LTA4LTMwVDIzOjA5OjQxLjM3OVoiIE5vdE9uT3JBZnRlcj0iMjAxNy0wOC0zMFQyMzoxOTo0MS4zNzlaIj4KICAgICAgICAgICAgPHNhbWwyOkF1ZGllbmNlUmVzdHJpY3Rpb24+CiAgICAgICAgICAgICAgICA8c2FtbDI6QXVkaWVuY2U+aHR0cHM6Ly9zYW1sLnNwLm5vcGUvc2Vzc2lvbi9zc28vc2FtbC9zcGVudGl0eWlkL3JxNWp3a3ZiOHo8L3NhbWwyOkF1ZGllbmNlPgogICAgICAgICAgICA8L3NhbWwyOkF1ZGllbmNlUmVzdHJpY3Rpb24+CiAgICAgICAgPC9zYW1sMjpDb25kaXRpb25zPgogICAgICAgIDxzYW1sMjpBdXRoblN0YXRlbWVudCB4bWxuczpzYW1sMj0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFzc2VydGlvbiIgQXV0aG5JbnN0YW50PSIyMDE3LTA4LTMwVDIzOjE0OjQxLjM3OVoiIFNlc3Npb25JbmRleD0iaWQtNTMxYjJiZjAyYWUwYTU4OGMzMDIzNDE2M2FkYmZkOWQ2YmJmMDc0NyI+CiAgICAgICAgICAgIDxzYW1sMjpBdXRobkNvbnRleHQ+CiAgICAgICAgICAgICAgICA8c2FtbDI6QXV0aG5Db250ZXh0Q2xhc3NSZWY+dXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFjOmNsYXNzZXM6UGFzc3dvcmRQcm90ZWN0ZWRUcmFuc3BvcnQ8L3NhbWwyOkF1dGhuQ29udGV4dENsYXNzUmVmPgogICAgICAgICAgICA8L3NhbWwyOkF1dGhuQ29udGV4dD4KICAgICAgICA8L3NhbWwyOkF1dGhuU3RhdGVtZW50PgogICAgICAgIDxzYW1sMjpBdHRyaWJ1dGVTdGF0ZW1lbnQgeG1sbnM6c2FtbDI9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iPgogICAgICAgICAgICA8c2FtbDI6QXR0cmlidXRlIE5hbWU9ImdpdmVuTmFtZSIgTmFtZUZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmF0dHJuYW1lLWZvcm1hdDp1bnNwZWNpZmllZCI+CiAgICAgICAgICAgICAgICA8c2FtbDI6QXR0cmlidXRlVmFsdWUgeG1sbnM6eHM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hIiB4bWxuczp4c2k9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hLWluc3RhbmNlIiB4c2k6dHlwZT0ieHM6c3RyaW5nIj5GaXJzdDwvc2FtbDI6QXR0cmlidXRlVmFsdWU+CiAgICAgICAgICAgIDwvc2FtbDI6QXR0cmlidXRlPgogICAgICAgICAgICA8c2FtbDI6QXR0cmlidXRlIE5hbWU9InNuIiBOYW1lRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXR0cm5hbWUtZm9ybWF0OnVuc3BlY2lmaWVkIj4KICAgICAgICAgICAgICAgIDxzYW1sMjpBdHRyaWJ1dGVWYWx1ZSB4bWxuc
Download .txt
gitextract_zio5e6rg/

├── .github/
│   ├── dependabot.yml
│   └── workflows/
│       ├── scorecard.yml
│       └── test.yml
├── .gitignore
├── LICENSE
├── README.md
├── SECURITY.md
├── attribute.go
├── authn_request.go
├── build_logout_response.go
├── build_request.go
├── build_request_test.go
├── decode_logout_request.go
├── decode_response.go
├── decode_response_test.go
├── go.mod
├── go.sum
├── internal/
│   └── fuzz/
│       ├── README.md
│       └── fuzz_test.go
├── logout_request.go
├── oss-fuzz/
│   ├── Dockerfile
│   ├── README.md
│   ├── build.sh
│   ├── fuzz_decode_response.options
│   └── project.yaml
├── providertests/
│   ├── exercise.go
│   ├── exercise_go_1_6.go
│   ├── oktadev_test.go
│   ├── onelogin_test.go
│   ├── pingfed_test.go
│   ├── providers_test.go
│   ├── testdata/
│   │   ├── adfs_idp_signing_cert.pem
│   │   ├── adfs_response.b64
│   │   ├── adfs_response_enc.b64
│   │   ├── auth0_cert.pem
│   │   ├── auth0_response.xml
│   │   ├── oam_cert.pem
│   │   ├── oam_response.xml
│   │   ├── okta_cert.pem
│   │   ├── okta_response.xml
│   │   ├── oktaenc_idp_signing_cert.pem
│   │   ├── oktaenc_response.b64
│   │   ├── oktaenc_sp_encryption_cert.pem
│   │   ├── oktaenc_sp_encryption_key.pem
│   │   ├── oktaenc_sp_signing_cert.pem
│   │   ├── oktaenc_sp_signing_key.pem
│   │   ├── onelogin/
│   │   │   ├── idp.signing.cert
│   │   │   ├── olgn09_response_01.b64
│   │   │   ├── olgn09_response_03.b64
│   │   │   ├── olgn09_response_04.b64
│   │   │   ├── olgn09_response_05.b64
│   │   │   ├── olgn09_response_06.b64
│   │   │   ├── olgn09_response_07.b64
│   │   │   ├── olgn09_response_08.b64
│   │   │   ├── olgn09_response_09.b64
│   │   │   ├── olgn09_response_11.b64
│   │   │   ├── olgn09_response_12.b64
│   │   │   ├── olgn09_response_13.b64
│   │   │   ├── olgn09_response_14.b64
│   │   │   ├── olgn09_response_15.b64
│   │   │   ├── olgn09_response_155.b64
│   │   │   ├── olgn09_response_156.b64
│   │   │   ├── olgn09_response_157.b64
│   │   │   ├── olgn09_response_158.b64
│   │   │   ├── olgn09_response_159.b64
│   │   │   ├── olgn09_response_16.b64
│   │   │   ├── olgn09_response_17.b64
│   │   │   ├── olgn09_response_18.b64
│   │   │   ├── olgn09_response_21.b64
│   │   │   ├── olgn09_response_22.b64
│   │   │   ├── olgn09_response_23.b64
│   │   │   ├── olgn09_response_24.b64
│   │   │   ├── olgn09_response_25.b64
│   │   │   ├── olgn09_response_26.b64
│   │   │   ├── olgn09_response_27.b64
│   │   │   ├── olgn09_response_28.b64
│   │   │   ├── olgn09_response_31.b64
│   │   │   ├── olgn09_response_33.b64
│   │   │   ├── olgn09_response_34.b64
│   │   │   ├── olgn09_response_36.b64
│   │   │   ├── olgn09_response_37.b64
│   │   │   ├── olgn09_response_38.b64
│   │   │   ├── olgn09_response_46.b64
│   │   │   ├── olgn09_response_47.b64
│   │   │   ├── olgn09_response_48.b64
│   │   │   ├── olgn09_response_50.b64
│   │   │   ├── olgn09_response_51.b64
│   │   │   ├── olgn09_response_52.b64
│   │   │   ├── olgn09_response_53.b64
│   │   │   ├── olgn09_response_54.b64
│   │   │   ├── olgn09_response_55.b64
│   │   │   ├── olgn09_response_56.b64
│   │   │   ├── olgn09_response_57.b64
│   │   │   ├── olgn09_response_58.b64
│   │   │   ├── olgn09_response_59.b64
│   │   │   ├── olgn09_response_81.b64
│   │   │   ├── olgn09_response_82.b64
│   │   │   ├── olgn09_response_83.b64
│   │   │   ├── olgn09_response_84.b64
│   │   │   ├── olgn09_response_85.b64
│   │   │   ├── olgn09_response_86.b64
│   │   │   ├── olgn09_response_87.b64
│   │   │   ├── olgn09_response_88.b64
│   │   │   ├── olgn09_response_89.b64
│   │   │   ├── olgn09_response_91.b64
│   │   │   ├── olgn09_response_92.b64
│   │   │   ├── olgn09_response_93.b64
│   │   │   ├── olgn09_response_94.b64
│   │   │   ├── olgn09_response_97.b64
│   │   │   ├── olgn09_response_98.b64
│   │   │   ├── olgn09_response_99.b64
│   │   │   ├── sp.encryption.cert
│   │   │   ├── sp.encryption.key
│   │   │   ├── sp.signing.cert
│   │   │   └── sp.signing.key
│   │   ├── onelogin_cert.pem
│   │   ├── onelogin_response.xml
│   │   ├── pingfed/
│   │   │   ├── idp.signing.cert
│   │   │   ├── pfed11_response_01.b64
│   │   │   ├── pfed11_response_02.b64
│   │   │   ├── pfed11_response_03.b64
│   │   │   ├── pfed11_response_05.b64
│   │   │   ├── sp.encryption.cert
│   │   │   ├── sp.encryption.key
│   │   │   ├── sp.signing.cert
│   │   │   └── sp.signing.key
│   │   └── saml.oktadev.com/
│   │       ├── oktadev.pem
│   │       ├── response_0
│   │       ├── response_1
│   │       ├── response_10
│   │       ├── response_11
│   │       ├── response_12
│   │       ├── response_13
│   │       ├── response_14
│   │       ├── response_15
│   │       ├── response_16
│   │       ├── response_2
│   │       ├── response_3
│   │       ├── response_4
│   │       ├── response_5
│   │       ├── response_6
│   │       ├── response_7
│   │       ├── response_8
│   │       └── response_9
│   └── utils.go
├── retrieve_assertion.go
├── run_test.sh
├── s2example/
│   └── demo.go
├── saml.go
├── saml_test.go
├── test_constants.go
├── testdata/
│   ├── assertion.xml
│   ├── idp.crt
│   ├── saml.post
│   ├── saml.xml
│   ├── saml_compressed.post
│   ├── test.crt
│   └── test.key
├── types/
│   ├── encrypted_assertion.go
│   ├── encrypted_key.go
│   ├── metadata.go
│   └── response.go
├── uuid/
│   ├── uuid.go
│   └── uuid_test.go
├── validate.go
└── xml_constants.go
Download .txt
SYMBOL INDEX (264 symbols across 30 files)

FILE: attribute.go
  type Values (line 21) | type Values
    method Get (line 25) | func (vals Values) Get(k string) string {
    method GetSize (line 37) | func (vals Values) GetSize(k string) int {
    method GetAll (line 52) | func (vals Values) GetAll(k string) []string {

FILE: authn_request.go
  type AuthNRequest (line 20) | type AuthNRequest struct

FILE: build_logout_response.go
  method buildLogoutResponse (line 26) | func (sp *SAMLServiceProvider) buildLogoutResponse(statusCodeValue strin...
  method BuildLogoutResponseDocument (line 71) | func (sp *SAMLServiceProvider) BuildLogoutResponseDocument(status string...
  method BuildLogoutResponseDocumentNoSig (line 75) | func (sp *SAMLServiceProvider) BuildLogoutResponseDocumentNoSig(status s...
  method SignLogoutResponse (line 79) | func (sp *SAMLServiceProvider) SignLogoutResponse(el *etree.Element) (*e...
  method buildLogoutResponseBodyPostFromDocument (line 98) | func (sp *SAMLServiceProvider) buildLogoutResponseBodyPostFromDocument(r...
  method BuildLogoutResponseBodyPostFromDocument (line 156) | func (sp *SAMLServiceProvider) BuildLogoutResponseBodyPostFromDocument(r...

FILE: build_request.go
  constant issueInstantFormat (line 30) | issueInstantFormat = "2006-01-02T15:04:05Z"
  method buildAuthnRequest (line 32) | func (sp *SAMLServiceProvider) buildAuthnRequest(includeSig bool) (*etre...
  method BuildAuthRequestDocument (line 97) | func (sp *SAMLServiceProvider) BuildAuthRequestDocument() (*etree.Docume...
  method BuildAuthRequestDocumentNoSig (line 101) | func (sp *SAMLServiceProvider) BuildAuthRequestDocumentNoSig() (*etree.D...
  method SignAuthnRequest (line 110) | func (sp *SAMLServiceProvider) SignAuthnRequest(el *etree.Element) (*etr...
  method BuildAuthRequest (line 130) | func (sp *SAMLServiceProvider) BuildAuthRequest() (string, error) {
  method buildAuthURLFromDocument (line 138) | func (sp *SAMLServiceProvider) buildAuthURLFromDocument(relayState, bind...
  method BuildAuthURLFromDocument (line 192) | func (sp *SAMLServiceProvider) BuildAuthURLFromDocument(relayState strin...
  method BuildAuthURLRedirect (line 196) | func (sp *SAMLServiceProvider) BuildAuthURLRedirect(relayState string, d...
  method buildAuthBodyPostFromDocument (line 200) | func (sp *SAMLServiceProvider) buildAuthBodyPostFromDocument(relayState ...
  method BuildAuthBodyPost (line 258) | func (sp *SAMLServiceProvider) BuildAuthBodyPost(relayState string) ([]b...
  method BuildAuthBodyPostFromDocument (line 277) | func (sp *SAMLServiceProvider) BuildAuthBodyPostFromDocument(relayState ...
  method BuildAuthURL (line 282) | func (sp *SAMLServiceProvider) BuildAuthURL(relayState string) (string, ...
  method AuthRedirect (line 293) | func (sp *SAMLServiceProvider) AuthRedirect(w http.ResponseWriter, r *ht...
  method buildLogoutRequest (line 303) | func (sp *SAMLServiceProvider) buildLogoutRequest(includeSig bool, nameI...
  method SignLogoutRequest (line 358) | func (sp *SAMLServiceProvider) SignLogoutRequest(el *etree.Element) (*et...
  method BuildLogoutRequestDocumentNoSig (line 377) | func (sp *SAMLServiceProvider) BuildLogoutRequestDocumentNoSig(nameID st...
  method BuildLogoutRequestDocument (line 381) | func (sp *SAMLServiceProvider) BuildLogoutRequestDocument(nameID string,...
  method BuildLogoutBodyPostFromDocument (line 387) | func (sp *SAMLServiceProvider) BuildLogoutBodyPostFromDocument(relayStat...
  method buildLogoutBodyPostFromDocument (line 391) | func (sp *SAMLServiceProvider) buildLogoutBodyPostFromDocument(relayStat...
  method BuildLogoutURLRedirect (line 447) | func (sp *SAMLServiceProvider) BuildLogoutURLRedirect(relayState string,...
  method buildLogoutURLFromDocument (line 451) | func (sp *SAMLServiceProvider) buildLogoutURLFromDocument(relayState, bi...
  function signatureInputString (line 540) | func signatureInputString(samlRequest, relayState, sigAlg string) string {

FILE: build_request_test.go
  function TestRedirect (line 33) | func TestRedirect(t *testing.T) {
  function TestRequestedAuthnContextOmitted (line 81) | func TestRequestedAuthnContextOmitted(t *testing.T) {
  function TestRequestedAuthnContextIncluded (line 102) | func TestRequestedAuthnContextIncluded(t *testing.T) {
  function TestForceAuthnOmitted (line 133) | func TestForceAuthnOmitted(t *testing.T) {
  function TestForceAuthnIncluded (line 153) | func TestForceAuthnIncluded(t *testing.T) {
  function TestIsPassiveOmitted (line 175) | func TestIsPassiveOmitted(t *testing.T) {
  function TestIsPassiveIncluded (line 195) | func TestIsPassiveIncluded(t *testing.T) {

FILE: decode_logout_request.go
  method validateLogoutRequestAttributes (line 24) | func (sp *SAMLServiceProvider) validateLogoutRequestAttributes(request *...
  method ValidateEncodedLogoutRequestPOST (line 45) | func (sp *SAMLServiceProvider) ValidateEncodedLogoutRequestPOST(encodedR...

FILE: decode_response.go
  constant defaultMaxDecompressedResponseSize (line 36) | defaultMaxDecompressedResponseSize = 5 * 1024 * 1024
  method validationContext (line 39) | func (sp *SAMLServiceProvider) validationContext() *dsig.ValidationConte...
  method validateResponseAttributes (line 47) | func (sp *SAMLServiceProvider) validateResponseAttributes(response *type...
  method validateLogoutResponseAttributes (line 70) | func (sp *SAMLServiceProvider) validateLogoutResponseAttributes(response...
  function xmlUnmarshalElement (line 91) | func xmlUnmarshalElement(el *etree.Element, obj interface{}) error {
  method getDecryptCert (line 106) | func (sp *SAMLServiceProvider) getDecryptCert() (*tls.Certificate, error) {
  method decryptAssertions (line 150) | func (sp *SAMLServiceProvider) decryptAssertions(el *etree.Element) error {
  method validateElementSignature (line 202) | func (sp *SAMLServiceProvider) validateElementSignature(el *etree.Elemen...
  method validateAssertionSignatures (line 207) | func (sp *SAMLServiceProvider) validateAssertionSignatures(el *etree.Ele...
  method verifyAssertionSignaturesIfPresent (line 260) | func (sp *SAMLServiceProvider) verifyAssertionSignaturesIfPresent(respon...
  method ValidateEncodedResponse (line 294) | func (sp *SAMLServiceProvider) ValidateEncodedResponse(encodedResponse s...
  function DecodeUnverifiedBaseResponse (line 448) | func DecodeUnverifiedBaseResponse(encodedResponse string) (*types.Unveri...
  function maybeDeflate (line 470) | func maybeDeflate(data []byte, maxSize int64, decoder func([]byte) error...
  function parseResponse (line 496) | func parseResponse(xml []byte, maxSize int64) (*etree.Document, *etree.E...
  function DecodeUnverifiedLogoutResponse (line 524) | func DecodeUnverifiedLogoutResponse(encodedResponse string) (*types.Logo...
  method ValidateEncodedLogoutResponsePOST (line 543) | func (sp *SAMLServiceProvider) ValidateEncodedLogoutResponsePOST(encoded...

FILE: decode_response_test.go
  constant idpCert (line 34) | idpCert = `
  constant oktaCert (line 57) | oktaCert = `
  constant oktaCert2 (line 78) | oktaCert2 = `
  constant badInput (line 100) | badInput = `<saml2:Assertion ID="id1684056077776386493060641"IssueInstan...
  function testEncryptedAssertion (line 103) | func testEncryptedAssertion(t *testing.T, validateEncryptionCert bool) {
  function TestEncryptedAssertion (line 135) | func TestEncryptedAssertion(t *testing.T) {
  function TestEncryptedAssertionInvalidCert (line 139) | func TestEncryptedAssertionInvalidCert(t *testing.T) {
  function TestCompressedResponse (line 143) | func TestCompressedResponse(t *testing.T) {
  function TestDecodeColonsInLocalNames (line 165) | func TestDecodeColonsInLocalNames(t *testing.T) {
  function TestDecodeDoubleColonInjectionAttackResponse (line 176) | func TestDecodeDoubleColonInjectionAttackResponse(t *testing.T) {
  function TestMalFormedInput (line 187) | func TestMalFormedInput(t *testing.T) {
  function TestCompressionBombInput (line 208) | func TestCompressionBombInput(t *testing.T) {

FILE: internal/fuzz/fuzz_test.go
  function FuzzDecodeResponse (line 25) | func FuzzDecodeResponse(f *testing.F) {
  function FuzzLogoutResponse (line 39) | func FuzzLogoutResponse(f *testing.F) {
  function FuzzBuildRequest (line 53) | func FuzzBuildRequest(f *testing.F) {

FILE: logout_request.go
  type LogoutRequest (line 24) | type LogoutRequest struct

FILE: providertests/exercise.go
  function ExerciseProviderTestScenarios (line 26) | func ExerciseProviderTestScenarios(t *testing.T, scenarios []ProviderTes...

FILE: providertests/exercise_go_1_6.go
  function ExerciseProviderTestScenarios (line 26) | func ExerciseProviderTestScenarios(t *testing.T, scenarios []ProviderTes...

FILE: providertests/oktadev_test.go
  function TestOktaDevCasesLocally (line 51) | func TestOktaDevCasesLocally(t *testing.T) {

FILE: providertests/onelogin_test.go
  function TestOneLoginCasesLocally (line 190) | func TestOneLoginCasesLocally(t *testing.T) {

FILE: providertests/pingfed_test.go
  function TestPingFedCasesLocally (line 56) | func TestPingFedCasesLocally(t *testing.T) {

FILE: providertests/providers_test.go
  function TestValidateResponses (line 26) | func TestValidateResponses(t *testing.T) {

FILE: providertests/utils.go
  function scenarioIndexes (line 36) | func scenarioIndexes(errs map[int]string, warns map[int]scenarioWarnings...
  type scenarioWarnings (line 47) | type scenarioWarnings struct
  function scenarioErrorChecker (line 52) | func scenarioErrorChecker(i int, scenarioErrors map[int]string) func(*te...
  function scenarioWarningChecker (line 62) | func scenarioWarningChecker(i int, scenarioWarns map[int]scenarioWarning...
  function LoadXMLResponse (line 70) | func LoadXMLResponse(path string) string {
  function LoadRawResponse (line 79) | func LoadRawResponse(path string) string {
  function LoadKeyStore (line 88) | func LoadKeyStore(certPath, keyPath string) (ks dsig.TLSCertKeyStore) {
  function LoadCertificateStore (line 101) | func LoadCertificateStore(path string) dsig.X509CertificateStore {
  type ProviderTestScenario (line 122) | type ProviderTestScenario struct
  function getAtTime (line 130) | func getAtTime(idx int, scenarioAtTimes map[int]string) (atTime time.Tim...
  function spAtTime (line 139) | func spAtTime(template *saml2.SAMLServiceProvider, atTime time.Time, raw...

FILE: retrieve_assertion.go
  type ErrMissingElement (line 22) | type ErrMissingElement struct
    method Error (line 40) | func (e ErrMissingElement) Error() string {
  type ErrVerification (line 26) | type ErrVerification struct
    method Error (line 30) | func (e ErrVerification) Error() string {
  method RetrieveAssertionInfo (line 49) | func (sp *SAMLServiceProvider) RetrieveAssertionInfo(encodedResponse str...

FILE: s2example/demo.go
  function main (line 32) | func main() {

FILE: saml.go
  type ErrSaml (line 28) | type ErrSaml struct
    method Error (line 33) | func (serr ErrSaml) Error() string {
  type SAMLServiceProvider (line 40) | type SAMLServiceProvider struct
    method SetSPKeyStore (line 100) | func (sp *SAMLServiceProvider) SetSPKeyStore(ks *KeyStore) error {
    method SetSPSigningKeyStore (line 109) | func (sp *SAMLServiceProvider) SetSPSigningKeyStore(ks *KeyStore) error {
    method Metadata (line 138) | func (sp *SAMLServiceProvider) Metadata() (*types.EntityDescriptor, er...
    method MetadataWithSLO (line 197) | func (sp *SAMLServiceProvider) MetadataWithSLO(validityHours int64) (*...
    method GetEncryptionKey (line 262) | func (sp *SAMLServiceProvider) GetEncryptionKey() dsig.X509KeyStore {
    method GetSigningKey (line 267) | func (sp *SAMLServiceProvider) GetSigningKey() dsig.X509KeyStore {
    method getEncryptionCert (line 274) | func (sp *SAMLServiceProvider) getEncryptionCert() ([]byte, error) {
    method GetEncryptionCertBytes (line 285) | func (sp *SAMLServiceProvider) GetEncryptionCertBytes() ([]byte, error) {
    method getSigningCert (line 296) | func (sp *SAMLServiceProvider) getSigningCert() ([]byte, error) {
    method GetSigningCertBytes (line 307) | func (sp *SAMLServiceProvider) GetSigningCertBytes() ([]byte, error) {
    method getSignerCert (line 318) | func (sp *SAMLServiceProvider) getSignerCert() (crypto.Signer, []byte,...
    method SigningContext (line 328) | func (sp *SAMLServiceProvider) SigningContext() *dsig.SigningContext {
  type KeyStore (line 117) | type KeyStore struct
  type RequestedAuthnContext (line 125) | type RequestedAuthnContext struct
  type ProxyRestriction (line 367) | type ProxyRestriction struct
  type WarningInfo (line 372) | type WarningInfo struct
  type AssertionInfo (line 379) | type AssertionInfo struct

FILE: saml_test.go
  function init (line 46) | func init() {
  function TestDecode (line 56) | func TestDecode(t *testing.T) {
  type testKeyStoreImpl (line 98) | type testKeyStoreImpl struct
    method GetKeyPair (line 103) | func (ks *testKeyStoreImpl) GetKeyPair() (*rsa.PrivateKey, []byte, err...
  function testKeyStore (line 109) | func testKeyStore(t *testing.T, validAt time.Time) dsig.X509KeyStore {
  function signResponse (line 128) | func signResponse(t *testing.T, resp string, sp *SAMLServiceProvider) st...
  function getSAMLServiceProvider (line 160) | func getSAMLServiceProvider(t *testing.T, _cert []byte) *SAMLServiceProv...
  function TestSAML (line 189) | func TestSAML(t *testing.T) {
  function TestSAMLUsingSetSPKeyStore (line 199) | func TestSAMLUsingSetSPKeyStore(t *testing.T) {
  function testSAMLServiceProvider (line 212) | func testSAMLServiceProvider(t *testing.T, sp *SAMLServiceProvider) {
  function TestInvalidResponseBadBase64 (line 345) | func TestInvalidResponseBadBase64(t *testing.T) {
  function TestInvalidResponseBadCompression (line 353) | func TestInvalidResponseBadCompression(t *testing.T) {
  function TestInvalidResponseBadXML (line 367) | func TestInvalidResponseBadXML(t *testing.T) {
  function TestInvalidResponseNoElement (line 385) | func TestInvalidResponseNoElement(t *testing.T) {
  function TestSAMLCommentInjection (line 394) | func TestSAMLCommentInjection(t *testing.T) {

FILE: test_constants.go
  constant rawResponse (line 39) | rawResponse = `
  constant manInTheMiddledResponse (line 58) | manInTheMiddledResponse = `
  constant alteredReferenceURIResponse (line 93) | alteredReferenceURIResponse = `
  constant alteredSignedInfoResponse (line 128) | alteredSignedInfoResponse = `
  constant alteredRecipientResponse (line 163) | alteredRecipientResponse = `
  constant alteredSubjectConfirmationMethodResponse (line 182) | alteredSubjectConfirmationMethodResponse = `
  constant alteredDestinationResponse (line 201) | alteredDestinationResponse = `
  constant alteredVersionResponse (line 220) | alteredVersionResponse = `
  constant missingIDResponse (line 239) | missingIDResponse = `
  constant assertionInfoResponse (line 274) | assertionInfoResponse = `
  constant assertionInfoModifiedAudienceResponse (line 293) | assertionInfoModifiedAudienceResponse = `
  constant assertionInfoOneTimeUseResponse (line 312) | assertionInfoOneTimeUseResponse = `
  constant assertionInfoProxyRestrictionResponse (line 331) | assertionInfoProxyRestrictionResponse = `
  constant assertionInfoProxyRestrictionNoCountResponse (line 350) | assertionInfoProxyRestrictionNoCountResponse = `
  constant assertionInfoProxyRestrictionNoAudienceResponse (line 369) | assertionInfoProxyRestrictionNoAudienceResponse = `
  constant exampleBase64 (line 388) | exampleBase64 = `PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48c2F...
  constant exampleBase64_2 (line 390) | exampleBase64_2 = `PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48c...
  constant commentInjectionAttackResponse (line 392) | commentInjectionAttackResponse = `
  constant doubleColonAssertionInjectionAttackResponse (line 412) | doubleColonAssertionInjectionAttackResponse = `

FILE: types/encrypted_assertion.go
  type EncryptedAssertion (line 26) | type EncryptedAssertion struct
    method DecryptBytes (line 34) | func (ea *EncryptedAssertion) DecryptBytes(cert *tls.Certificate) ([]b...
    method Decrypt (line 95) | func (ea *EncryptedAssertion) Decrypt(cert *tls.Certificate) (*Asserti...

FILE: types/encrypted_key.go
  type EncryptedKey (line 36) | type EncryptedKey struct
    method DecryptSymmetricKey (line 104) | func (ek *EncryptedKey) DecryptSymmetricKey(cert *tls.Certificate) (ci...
  type EncryptionMethod (line 44) | type EncryptionMethod struct
  type DigestMethod (line 55) | type DigestMethod struct
  constant MethodRSAOAEP (line 61) | MethodRSAOAEP  = "http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p"
  constant MethodRSAOAEP2 (line 62) | MethodRSAOAEP2 = "http://www.w3.org/2009/xmlenc11#rsa-oaep"
  constant MethodRSAv1_5 (line 63) | MethodRSAv1_5  = "http://www.w3.org/2001/04/xmlenc#rsa-1_5"
  constant MethodAES128GCM (line 68) | MethodAES128GCM    = "http://www.w3.org/2009/xmlenc11#aes128-gcm"
  constant MethodAES192GCM (line 69) | MethodAES192GCM    = "http://www.w3.org/2009/xmlenc11#aes192-gcm"
  constant MethodAES256GCM (line 70) | MethodAES256GCM    = "http://www.w3.org/2009/xmlenc11#aes256-gcm"
  constant MethodAES128CBC (line 71) | MethodAES128CBC    = "http://www.w3.org/2001/04/xmlenc#aes128-cbc"
  constant MethodAES256CBC (line 72) | MethodAES256CBC    = "http://www.w3.org/2001/04/xmlenc#aes256-cbc"
  constant MethodTripleDESCBC (line 73) | MethodTripleDESCBC = "http://www.w3.org/2001/04/xmlenc#tripledes-cbc"
  constant MethodSHA1 (line 78) | MethodSHA1   = "http://www.w3.org/2000/09/xmldsig#sha1"
  constant MethodSHA256 (line 79) | MethodSHA256 = "http://www.w3.org/2000/09/xmldsig#sha256"
  constant MethodSHA512 (line 80) | MethodSHA512 = "http://www.w3.org/2000/09/xmldsig#sha512"
  function debugKeyFp (line 85) | func debugKeyFp(keyBytes []byte) string {

FILE: types/metadata.go
  type EntityDescriptor (line 24) | type EntityDescriptor struct
  type Endpoint (line 34) | type Endpoint struct
  type IndexedEndpoint (line 40) | type IndexedEndpoint struct
  type SPSSODescriptor (line 46) | type SPSSODescriptor struct
  type IDPSSODescriptor (line 58) | type IDPSSODescriptor struct
  type KeyDescriptor (line 69) | type KeyDescriptor struct
  type NameIDFormat (line 76) | type NameIDFormat struct
  type SingleSignOnService (line 81) | type SingleSignOnService struct
  type SingleLogoutService (line 87) | type SingleLogoutService struct
  type SigningMethod (line 93) | type SigningMethod struct
  type Extensions (line 99) | type Extensions struct

FILE: types/response.go
  type UnverifiedBaseResponse (line 27) | type UnverifiedBaseResponse struct
  type Response (line 36) | type Response struct
  type LogoutResponse (line 50) | type LogoutResponse struct
  type Status (line 62) | type Status struct
  type StatusCode (line 67) | type StatusCode struct
  type Issuer (line 72) | type Issuer struct
  type Signature (line 77) | type Signature struct
  type Assertion (line 81) | type Assertion struct
  type Subject (line 95) | type Subject struct
  type AuthnContext (line 101) | type AuthnContext struct
  type AuthnContextClassRef (line 106) | type AuthnContextClassRef struct
  type NameID (line 111) | type NameID struct
  type SubjectConfirmation (line 116) | type SubjectConfirmation struct
  type SubjectConfirmationData (line 122) | type SubjectConfirmationData struct
  type Conditions (line 129) | type Conditions struct
  type AudienceRestriction (line 138) | type AudienceRestriction struct
  type Audience (line 143) | type Audience struct
  type OneTimeUse (line 148) | type OneTimeUse struct
  type ProxyRestriction (line 152) | type ProxyRestriction struct
  type AttributeStatement (line 158) | type AttributeStatement struct
  type Attribute (line 163) | type Attribute struct
  type AttributeValue (line 171) | type AttributeValue struct
  type AuthnStatement (line 177) | type AuthnStatement struct

FILE: uuid/uuid.go
  type UUID (line 24) | type UUID
    method String (line 39) | func (u *UUID) String() string {
  function NewV4 (line 27) | func NewV4() *UUID {

FILE: uuid/uuid_test.go
  function TestUUID (line 21) | func TestUUID(t *testing.T) {

FILE: validate.go
  type ErrParsing (line 27) | type ErrParsing struct
    method Error (line 31) | func (ep ErrParsing) Error() string {
  constant ReasonUnsupported (line 37) | ReasonUnsupported = "Unsupported"
  constant ReasonExpired (line 38) | ReasonExpired     = "Expired"
  type ErrInvalidValue (line 43) | type ErrInvalidValue struct
    method Error (line 48) | func (e ErrInvalidValue) Error() string {
  constant SubjMethodBearer (line 57) | SubjMethodBearer = "urn:oasis:names:tc:SAML:2.0:cm:bearer"
  method VerifyAssertionConditions (line 62) | func (sp *SAMLServiceProvider) VerifyAssertionConditions(assertion *type...
  method Validate (line 136) | func (sp *SAMLServiceProvider) Validate(response *types.Response) error {
  method ValidateDecodedLogoutResponse (line 247) | func (sp *SAMLServiceProvider) ValidateDecodedLogoutResponse(response *t...
  method ValidateDecodedLogoutRequest (line 288) | func (sp *SAMLServiceProvider) ValidateDecodedLogoutRequest(request *Log...

FILE: xml_constants.go
  constant ResponseTag (line 18) | ResponseTag                = "Response"
  constant AssertionTag (line 19) | AssertionTag               = "Assertion"
  constant EncryptedAssertionTag (line 20) | EncryptedAssertionTag      = "EncryptedAssertion"
  constant SubjectTag (line 21) | SubjectTag                 = "Subject"
  constant NameIdTag (line 22) | NameIdTag                  = "NameID"
  constant SubjectConfirmationTag (line 23) | SubjectConfirmationTag     = "SubjectConfirmation"
  constant SubjectConfirmationDataTag (line 24) | SubjectConfirmationDataTag = "SubjectConfirmationData"
  constant AttributeStatementTag (line 25) | AttributeStatementTag      = "AttributeStatement"
  constant AttributeValueTag (line 26) | AttributeValueTag          = "AttributeValue"
  constant ConditionsTag (line 27) | ConditionsTag              = "Conditions"
  constant AudienceRestrictionTag (line 28) | AudienceRestrictionTag     = "AudienceRestriction"
  constant AudienceTag (line 29) | AudienceTag                = "Audience"
  constant OneTimeUseTag (line 30) | OneTimeUseTag              = "OneTimeUse"
  constant ProxyRestrictionTag (line 31) | ProxyRestrictionTag        = "ProxyRestriction"
  constant IssuerTag (line 32) | IssuerTag                  = "Issuer"
  constant StatusTag (line 33) | StatusTag                  = "Status"
  constant StatusCodeTag (line 34) | StatusCodeTag              = "StatusCode"
  constant DestinationAttr (line 38) | DestinationAttr  = "Destination"
  constant VersionAttr (line 39) | VersionAttr      = "Version"
  constant IdAttr (line 40) | IdAttr           = "ID"
  constant MethodAttr (line 41) | MethodAttr       = "Method"
  constant RecipientAttr (line 42) | RecipientAttr    = "Recipient"
  constant NameAttr (line 43) | NameAttr         = "Name"
  constant NotBeforeAttr (line 44) | NotBeforeAttr    = "NotBefore"
  constant NotOnOrAfterAttr (line 45) | NotOnOrAfterAttr = "NotOnOrAfter"
  constant CountAttr (line 46) | CountAttr        = "Count"
  constant NameIdFormatPersistent (line 50) | NameIdFormatPersistent      = "urn:oasis:names:tc:SAML:2.0:nameid-format...
  constant NameIdFormatTransient (line 51) | NameIdFormatTransient       = "urn:oasis:names:tc:SAML:2.0:nameid-format...
  constant NameIdFormatEmailAddress (line 52) | NameIdFormatEmailAddress    = "urn:oasis:names:tc:SAML:1.1:nameid-format...
  constant NameIdFormatUnspecified (line 53) | NameIdFormatUnspecified     = "urn:oasis:names:tc:SAML:1.1:nameid-format...
  constant NameIdFormatX509SubjectName (line 54) | NameIdFormatX509SubjectName = "urn:oasis:names:tc:SAML:1.1:nameid-format...
  constant AuthnContextPasswordProtectedTransport (line 56) | AuthnContextPasswordProtectedTransport = "urn:oasis:names:tc:SAML:2.0:ac...
  constant AuthnPolicyMatchExact (line 58) | AuthnPolicyMatchExact   = "exact"
  constant AuthnPolicyMatchMinimum (line 59) | AuthnPolicyMatchMinimum = "minimum"
  constant AuthnPolicyMatchMaximum (line 60) | AuthnPolicyMatchMaximum = "maximum"
  constant AuthnPolicyMatchBetter (line 61) | AuthnPolicyMatchBetter  = "better"
  constant StatusCodeSuccess (line 63) | StatusCodeSuccess          = "urn:oasis:names:tc:SAML:2.0:status:Success"
  constant StatusCodePartialLogout (line 64) | StatusCodePartialLogout    = "urn:oasis:names:tc:SAML:2.0:status:Partial...
  constant StatusCodeUnknownPrincipal (line 65) | StatusCodeUnknownPrincipal = "urn:oasis:names:tc:SAML:2.0:status:Unknown...
  constant BindingHttpPost (line 67) | BindingHttpPost     = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
  constant BindingHttpRedirect (line 68) | BindingHttpRedirect = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
  constant SAMLAssertionNamespace (line 72) | SAMLAssertionNamespace = "urn:oasis:names:tc:SAML:2.0:assertion"
  constant SAMLProtocolNamespace (line 73) | SAMLProtocolNamespace  = "urn:oasis:names:tc:SAML:2.0:protocol"
Condensed preview — 166 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (1,250K chars).
[
  {
    "path": ".github/dependabot.yml",
    "chars": 208,
    "preview": "version: 2\nupdates:\n  - package-ecosystem: \"github-actions\"\n    directory: \"/\"\n    schedule:\n      interval: \"weekly\"\n\n "
  },
  {
    "path": ".github/workflows/scorecard.yml",
    "chars": 1823,
    "preview": "name: Scorecard supply-chain security\non:\n  # For Branch-Protection check. Only the default branch is supported. See\n  #"
  },
  {
    "path": ".github/workflows/test.yml",
    "chars": 495,
    "preview": "on: [push, pull_request]\nname: Test\npermissions:\n  contents: read\n\njobs:\n  test:\n    name: Test Go ${{ matrix.go }}\n    "
  },
  {
    "path": ".gitignore",
    "chars": 7,
    "preview": "*.test\n"
  },
  {
    "path": "LICENSE",
    "chars": 10142,
    "preview": "\n                                 Apache License\n                           Version 2.0, January 2004\n                  "
  },
  {
    "path": "README.md",
    "chars": 1067,
    "preview": "# gosaml2\n\n[![Build Status](https://github.com/russellhaering/gosaml2/actions/workflows/test.yml/badge.svg?branch=main)]"
  },
  {
    "path": "SECURITY.md",
    "chars": 212,
    "preview": "# Security Policy\n\n## Reporting a Vulnerability\n\nSecurity vulnerabilities can be reported using GitHub's [private vulner"
  },
  {
    "path": "attribute.go",
    "chars": 1818,
    "preview": "// Copyright 2016 Russell Haering et al.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you ma"
  },
  {
    "path": "authn_request.go",
    "chars": 1031,
    "preview": "// Copyright 2016 Russell Haering et al.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you ma"
  },
  {
    "path": "build_logout_response.go",
    "chars": 5344,
    "preview": "// Copyright 2016 Russell Haering et al.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you ma"
  },
  {
    "path": "build_request.go",
    "chars": 18190,
    "preview": "// Copyright 2016 Russell Haering et al.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you ma"
  },
  {
    "path": "build_request_test.go",
    "chars": 6257,
    "preview": "// Copyright 2016 Russell Haering et al.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you ma"
  },
  {
    "path": "decode_logout_request.go",
    "chars": 2337,
    "preview": "// Copyright 2016 Russell Haering et al.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you ma"
  },
  {
    "path": "decode_response.go",
    "chars": 17706,
    "preview": "// Copyright 2016 Russell Haering et al.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you ma"
  },
  {
    "path": "decode_response_test.go",
    "chars": 12401,
    "preview": "// Copyright 2016 Russell Haering et al.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you ma"
  },
  {
    "path": "go.mod",
    "chars": 477,
    "preview": "module github.com/russellhaering/gosaml2\n\ngo 1.25.0\n\ntoolchain go1.26.1\n\nrequire (\n\tgithub.com/beevik/etree v1.6.0\n\tgith"
  },
  {
    "path": "go.sum",
    "chars": 2685,
    "preview": "github.com/beevik/etree v1.6.0 h1:u8Kwy8pp9D9XeITj2Z0XtA5qqZEmtJtuXZRQi+j03eE=\ngithub.com/beevik/etree v1.6.0/go.mod h1:"
  },
  {
    "path": "internal/fuzz/README.md",
    "chars": 618,
    "preview": "# Internal Fuzzing for gosaml2\n\nThis directory contains fuzzing targets for gosaml2 that are used with Go's built-in fuz"
  },
  {
    "path": "internal/fuzz/fuzz_test.go",
    "chars": 2162,
    "preview": "// Copyright 2025 Russell Haering et al.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you ma"
  },
  {
    "path": "logout_request.go",
    "chars": 1261,
    "preview": "// Copyright 2016 Russell Haering et al.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you ma"
  },
  {
    "path": "oss-fuzz/Dockerfile",
    "chars": 878,
    "preview": "# Copyright 2025 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": "oss-fuzz/README.md",
    "chars": 1518,
    "preview": "# OSS-Fuzz Integration for gosaml2\n\nThis directory contains the configuration files necessary for integrating gosaml2 wi"
  },
  {
    "path": "oss-fuzz/build.sh",
    "chars": 2218,
    "preview": "#!/bin/bash -eu\n# Copyright 2025 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you ma"
  },
  {
    "path": "oss-fuzz/fuzz_decode_response.options",
    "chars": 28,
    "preview": "[libfuzzer]\nmax_len = 10240\n"
  },
  {
    "path": "oss-fuzz/project.yaml",
    "chars": 249,
    "preview": "homepage: \"https://github.com/russellhaering/gosaml2\"\nprimary_contact: \"russell.haering@gmail.com\"\nlanguage: go\nmain_rep"
  },
  {
    "path": "providertests/exercise.go",
    "chars": 1761,
    "preview": "// Copyright 2016 Russell Haering et al.\n// \n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you m"
  },
  {
    "path": "providertests/exercise_go_1_6.go",
    "chars": 1684,
    "preview": "// Copyright 2016 Russell Haering et al.\n// \n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you m"
  },
  {
    "path": "providertests/oktadev_test.go",
    "chars": 3352,
    "preview": "// Copyright 2016 Russell Haering et al.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you ma"
  },
  {
    "path": "providertests/onelogin_test.go",
    "chars": 14968,
    "preview": "// Copyright 2016 Russell Haering et al.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you ma"
  },
  {
    "path": "providertests/pingfed_test.go",
    "chars": 4004,
    "preview": "// Copyright 2016 Russell Haering et al.\n// \n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you m"
  },
  {
    "path": "providertests/providers_test.go",
    "chars": 7049,
    "preview": "// Copyright 2016 Russell Haering et al.\n// \n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you m"
  },
  {
    "path": "providertests/testdata/adfs_idp_signing_cert.pem",
    "chars": 1050,
    "preview": "-----BEGIN CERTIFICATE-----\nMIIC2jCCAcKgAwIBAgIQNdNUIOmoKrFJNQadPV+9sTANBgkqhkiG9w0BAQsFADAp\nMScwJQYDVQQDEx5BREZTIFNpZ25"
  },
  {
    "path": "providertests/testdata/adfs_response.b64",
    "chars": 5533,
    "preview": "PHNhbWxwOlJlc3BvbnNlIElEPSJfYjlkM2VhNzAtMmEwYy00MmI2LWI4ZjctNjU3YWRlYjJiYjA5IiBWZXJzaW9uPSIyLjAiIElzc3VlSW5zdGFudD0iMjAx"
  },
  {
    "path": "providertests/testdata/adfs_response_enc.b64",
    "chars": 8997,
    "preview": "PHNhbWxwOlJlc3BvbnNlIElEPSJfYWUyNDZkNGUtYjllMC00OWUzLTk3YTItYzJmMjM2NDA4YjllIiBWZXJzaW9uPSIyLjAiIElzc3VlSW5zdGFudD0iMjAx"
  },
  {
    "path": "providertests/testdata/auth0_cert.pem",
    "chars": 1104,
    "preview": "-----BEGIN CERTIFICATE-----\r\nMIIC9DCCAdygAwIBAgIJX9Qb0a2w33UjMA0GCSqGSIb3DQEBBQUAMCExHzAdBgNV\r\nBAMTFnNjYWxlZnQtdGVzdC5hd"
  },
  {
    "path": "providertests/testdata/auth0_response.xml",
    "chars": 6819,
    "preview": "<samlp:Response xmlns:samlp=\"urn:oasis:names:tc:SAML:2.0:protocol\" ID=\"_5376783a08fa6e021aa4\" InResponseTo=\"_e3ce5e05-4e"
  },
  {
    "path": "providertests/testdata/oam_cert.pem",
    "chars": 752,
    "preview": "-----BEGIN CERTIFICATE-----\nMIIB/jCCAWegAwIBAgIBCjANBgkqhkiG9w0BAQQFADAkMSIwIAYDVQQDExlkZWFv\nYW0tZGV2MDIuanBsLm5hc2EuZ29"
  },
  {
    "path": "providertests/testdata/oam_response.xml",
    "chars": 2899,
    "preview": "<samlp:Response xmlns:samlp=\"urn:oasis:names:tc:SAML:2.0:protocol\" xmlns:dsig=\"http://www.w3.org/2000/09/xmldsig#\" xmlns"
  },
  {
    "path": "providertests/testdata/okta_cert.pem",
    "chars": 1318,
    "preview": "-----BEGIN CERTIFICATE-----\nMIIDpDCCAoygAwIBAgIGAVLIBhAwMA0GCSqGSIb3DQEBBQUAMIGSMQswCQYDVQQGEwJVUzETMBEG\nA1UECAwKQ2FsaWZ"
  },
  {
    "path": "providertests/testdata/okta_response.xml",
    "chars": 7603,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?><saml2p:Response xmlns:saml2p=\"urn:oasis:names:tc:SAML:2.0:protocol\" Destination=\""
  },
  {
    "path": "providertests/testdata/oktaenc_idp_signing_cert.pem",
    "chars": 1331,
    "preview": "-----BEGIN CERTIFICATE-----\nMIIDrDCCApSgAwIBAgIGAVhBgiX+MA0GCSqGSIb3DQEBBQUAMIGWMQswCQYDVQQGEwJVUzETMBEG\nA1UECAwKQ2FsaWZ"
  },
  {
    "path": "providertests/testdata/oktaenc_response.b64",
    "chars": 18181,
    "preview": "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48c2FtbDJwOlJlc3BvbnNlIHhtbG5zOnNhbWwycD0idXJuOm9hc2lzOm5hbWVzOnRjOlNB"
  },
  {
    "path": "providertests/testdata/oktaenc_sp_encryption_cert.pem",
    "chars": 1184,
    "preview": "-----BEGIN CERTIFICATE-----\nMIIDPjCCAiagAwIBAgIJAOvpZtJNtQ/rMA0GCSqGSIb3DQEBCwUAMFUxCzAJBgNV\nBAYTAlVTMRMwEQYDVQQIEwpDYWx"
  },
  {
    "path": "providertests/testdata/oktaenc_sp_encryption_key.pem",
    "chars": 1704,
    "preview": "-----BEGIN PRIVATE KEY-----\nMIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCn1JXIsAzwifSV\n8tTbMSn6sjny0FxSihoDSikS8yw"
  },
  {
    "path": "providertests/testdata/oktaenc_sp_signing_cert.pem",
    "chars": 1184,
    "preview": "-----BEGIN CERTIFICATE-----\nMIIDPjCCAiagAwIBAgIJAKHPtM2MqZxXMA0GCSqGSIb3DQEBCwUAMFUxCzAJBgNV\nBAYTAlVTMRMwEQYDVQQIEwpDYWx"
  },
  {
    "path": "providertests/testdata/oktaenc_sp_signing_key.pem",
    "chars": 1704,
    "preview": "-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC1sPBD/3UhtMsu\n3C9rEL3Eaok67FQls0OUhoGTRmx"
  },
  {
    "path": "providertests/testdata/onelogin/idp.signing.cert",
    "chars": 1184,
    "preview": "-----BEGIN CERTIFICATE-----\nMIIDPjCCAiagAwIBAgIJAMQeWnDJVaTyMA0GCSqGSIb3DQEBCwUAMFUxCzAJBgNV\nBAYTAlVTMRMwEQYDVQQIEwpDYWx"
  },
  {
    "path": "providertests/testdata/onelogin/olgn09_response_01.b64",
    "chars": 8240,
    "preview": "PD94bWwgdmVyc2lvbj0iMS4wIj8+CjxzYW1sMnA6UmVzcG9uc2UgeG1sbnM6c2FtbDJwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wi"
  },
  {
    "path": "providertests/testdata/onelogin/olgn09_response_03.b64",
    "chars": 8212,
    "preview": "PHNhbWwycDpSZXNwb25zZSB4bWxuczpzYW1sMnA9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpwcm90b2NvbCIgRGVzdGluYXRpb249Imh0dHBzOi8v"
  },
  {
    "path": "providertests/testdata/onelogin/olgn09_response_04.b64",
    "chars": 11252,
    "preview": "PD94bWwgdmVyc2lvbj0iMS4wIj8+CjxzYW1sMnA6UmVzcG9uc2UgeG1sbnM6c2FtbDJwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wi"
  },
  {
    "path": "providertests/testdata/onelogin/olgn09_response_05.b64",
    "chars": 12176,
    "preview": "PHNhbWwycDpSZXNwb25zZSB4bWxuczpzYW1sMnA9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpwcm90b2NvbCIgRGVzdGluYXRpb249Imh0dHBzOi8v"
  },
  {
    "path": "providertests/testdata/onelogin/olgn09_response_06.b64",
    "chars": 13924,
    "preview": "PHNhbWwycDpSZXNwb25zZSB4bWxuczpzYW1sMnA9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpwcm90b2NvbCIgRGVzdGluYXRpb249Imh0dHBzOi8v"
  },
  {
    "path": "providertests/testdata/onelogin/olgn09_response_07.b64",
    "chars": 12952,
    "preview": "PD94bWwgdmVyc2lvbj0iMS4wIj8+CjxzYW1sMnA6UmVzcG9uc2UgeG1sbnM6c2FtbDJwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wi"
  },
  {
    "path": "providertests/testdata/onelogin/olgn09_response_08.b64",
    "chars": 16964,
    "preview": "PD94bWwgdmVyc2lvbj0iMS4wIj8+CjxzYW1sMnA6UmVzcG9uc2UgeG1sbnM6c2FtbDJwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wi"
  },
  {
    "path": "providertests/testdata/onelogin/olgn09_response_09.b64",
    "chars": 15216,
    "preview": "PD94bWwgdmVyc2lvbj0iMS4wIj8+CjxzYW1sMnA6UmVzcG9uc2UgeG1sbnM6c2FtbDJwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wi"
  },
  {
    "path": "providertests/testdata/onelogin/olgn09_response_11.b64",
    "chars": 8349,
    "preview": "PD94bWwgdmVyc2lvbj0iMS4wIj8+CjxzYW1sMnA6UmVzcG9uc2UgeG1sbnM6c2FtbDJwPSJ1cm46\nb2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2w"
  },
  {
    "path": "providertests/testdata/onelogin/olgn09_response_12.b64",
    "chars": 8349,
    "preview": "PD94bWwgdmVyc2lvbj0iMS4wIj8+CjxzYW1sMnA6UmVzcG9uc2UgeG1sbnM6c2FtbDJwPSJ1cm46\nb2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2w"
  },
  {
    "path": "providertests/testdata/onelogin/olgn09_response_13.b64",
    "chars": 8321,
    "preview": "PHNhbWwycDpSZXNwb25zZSB4bWxuczpzYW1sMnA9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIu\nMDpwcm90b2NvbCIgRGVzdGluYXRpb249Imh0dHBzOi8"
  },
  {
    "path": "providertests/testdata/onelogin/olgn09_response_14.b64",
    "chars": 11401,
    "preview": "PD94bWwgdmVyc2lvbj0iMS4wIj8+CjxzYW1sMnA6UmVzcG9uc2UgeG1sbnM6c2FtbDJwPSJ1cm46\nb2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2w"
  },
  {
    "path": "providertests/testdata/onelogin/olgn09_response_15.b64",
    "chars": 11401,
    "preview": "PD94bWwgdmVyc2lvbj0iMS4wIj8+CjxzYW1sMnA6UmVzcG9uc2UgeG1sbnM6c2FtbDJwPSJ1cm46\nb2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2w"
  },
  {
    "path": "providertests/testdata/onelogin/olgn09_response_155.b64",
    "chars": 11160,
    "preview": "PD94bWwgdmVyc2lvbj0iMS4wIj8+CjxzYW1sMnA6UmVzcG9uc2UgeG1sbnM6c2FtbDJwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wi"
  },
  {
    "path": "providertests/testdata/onelogin/olgn09_response_156.b64",
    "chars": 11248,
    "preview": "PD94bWwgdmVyc2lvbj0iMS4wIj8+CjxzYW1sMnA6UmVzcG9uc2UgeG1sbnM6c2FtbDJwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wi"
  },
  {
    "path": "providertests/testdata/onelogin/olgn09_response_157.b64",
    "chars": 11248,
    "preview": "PD94bWwgdmVyc2lvbj0iMS4wIj8+CjxzYW1sMnA6UmVzcG9uc2UgeG1sbnM6c2FtbDJwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wi"
  },
  {
    "path": "providertests/testdata/onelogin/olgn09_response_158.b64",
    "chars": 11248,
    "preview": "PD94bWwgdmVyc2lvbj0iMS4wIj8+CjxzYW1sMnA6UmVzcG9uc2UgeG1sbnM6c2FtbDJwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wi"
  },
  {
    "path": "providertests/testdata/onelogin/olgn09_response_159.b64",
    "chars": 11248,
    "preview": "PD94bWwgdmVyc2lvbj0iMS4wIj8+CjxzYW1sMnA6UmVzcG9uc2UgeG1sbnM6c2FtbDJwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wi"
  },
  {
    "path": "providertests/testdata/onelogin/olgn09_response_16.b64",
    "chars": 14108,
    "preview": "PHNhbWwycDpSZXNwb25zZSB4bWxuczpzYW1sMnA9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIu\nMDpwcm90b2NvbCIgRGVzdGluYXRpb249Imh0dHBzOi8"
  },
  {
    "path": "providertests/testdata/onelogin/olgn09_response_17.b64",
    "chars": 13123,
    "preview": "PD94bWwgdmVyc2lvbj0iMS4wIj8+CjxzYW1sMnA6UmVzcG9uc2UgeG1sbnM6c2FtbDJwPSJ1cm46\nb2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2w"
  },
  {
    "path": "providertests/testdata/onelogin/olgn09_response_18.b64",
    "chars": 16964,
    "preview": "PD94bWwgdmVyc2lvbj0iMS4wIj8+CjxzYW1sMnA6UmVzcG9uc2UgeG1sbnM6c2FtbDJwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wi"
  },
  {
    "path": "providertests/testdata/onelogin/olgn09_response_21.b64",
    "chars": 8212,
    "preview": "PD94bWwgdmVyc2lvbj0iMS4wIj8+CjxzYW1sMnA6UmVzcG9uc2UgeG1sbnM6c2FtbDJwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wi"
  },
  {
    "path": "providertests/testdata/onelogin/olgn09_response_22.b64",
    "chars": 8212,
    "preview": "PD94bWwgdmVyc2lvbj0iMS4wIj8+CjxzYW1sMnA6UmVzcG9uc2UgeG1sbnM6c2FtbDJwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wi"
  },
  {
    "path": "providertests/testdata/onelogin/olgn09_response_23.b64",
    "chars": 13892,
    "preview": "PHNhbWwycDpSZXNwb25zZSB4bWxuczpzYW1sMnA9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpwcm90b2NvbCIgRGVzdGluYXRpb249Imh0dHBzOi8v"
  },
  {
    "path": "providertests/testdata/onelogin/olgn09_response_24.b64",
    "chars": 13892,
    "preview": "PHNhbWwycDpSZXNwb25zZSB4bWxuczpzYW1sMnA9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpwcm90b2NvbCIgRGVzdGluYXRpb249Imh0dHBzOi8v"
  },
  {
    "path": "providertests/testdata/onelogin/olgn09_response_25.b64",
    "chars": 8240,
    "preview": "PD94bWwgdmVyc2lvbj0iMS4wIj8+CjxzYW1sMnA6UmVzcG9uc2UgeG1sbnM6c2FtbDJwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wi"
  },
  {
    "path": "providertests/testdata/onelogin/olgn09_response_26.b64",
    "chars": 8240,
    "preview": "PD94bWwgdmVyc2lvbj0iMS4wIj8+CjxzYW1sMnA6UmVzcG9uc2UgeG1sbnM6c2FtbDJwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wi"
  },
  {
    "path": "providertests/testdata/onelogin/olgn09_response_27.b64",
    "chars": 13924,
    "preview": "PHNhbWwycDpSZXNwb25zZSB4bWxuczpzYW1sMnA9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpwcm90b2NvbCIgRGVzdGluYXRpb249Imh0dHBzOi8v"
  },
  {
    "path": "providertests/testdata/onelogin/olgn09_response_28.b64",
    "chars": 13924,
    "preview": "PHNhbWwycDpSZXNwb25zZSB4bWxuczpzYW1sMnA9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpwcm90b2NvbCIgRGVzdGluYXRpb249Imh0dHBzOi8v"
  },
  {
    "path": "providertests/testdata/onelogin/olgn09_response_31.b64",
    "chars": 8240,
    "preview": "PD94bWwgdmVyc2lvbj0iMS4wIj8+CjxzYW1sMnA6UmVzcG9uc2UgeG1sbnM6c2FtbDJwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wi"
  },
  {
    "path": "providertests/testdata/onelogin/olgn09_response_33.b64",
    "chars": 8212,
    "preview": "PHNhbWwycDpSZXNwb25zZSB4bWxuczpzYW1sMnA9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpwcm90b2NvbCIgRGVzdGluYXRpb249Imh0dHBzOi8v"
  },
  {
    "path": "providertests/testdata/onelogin/olgn09_response_34.b64",
    "chars": 11252,
    "preview": "PD94bWwgdmVyc2lvbj0iMS4wIj8+CjxzYW1sMnA6UmVzcG9uc2UgeG1sbnM6c2FtbDJwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wi"
  },
  {
    "path": "providertests/testdata/onelogin/olgn09_response_36.b64",
    "chars": 13924,
    "preview": "PHNhbWwycDpSZXNwb25zZSB4bWxuczpzYW1sMnA9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpwcm90b2NvbCIgRGVzdGluYXRpb249Imh0dHBzOi8v"
  },
  {
    "path": "providertests/testdata/onelogin/olgn09_response_37.b64",
    "chars": 12952,
    "preview": "PD94bWwgdmVyc2lvbj0iMS4wIj8+CjxzYW1sMnA6UmVzcG9uc2UgeG1sbnM6c2FtbDJwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wi"
  },
  {
    "path": "providertests/testdata/onelogin/olgn09_response_38.b64",
    "chars": 16964,
    "preview": "PD94bWwgdmVyc2lvbj0iMS4wIj8+CjxzYW1sMnA6UmVzcG9uc2UgeG1sbnM6c2FtbDJwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wi"
  },
  {
    "path": "providertests/testdata/onelogin/olgn09_response_46.b64",
    "chars": 13924,
    "preview": "PHNhbWwycDpSZXNwb25zZSB4bWxuczpzYW1sMnA9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpwcm90b2NvbCIgRGVzdGluYXRpb249Imh0dHBzOi8v"
  },
  {
    "path": "providertests/testdata/onelogin/olgn09_response_47.b64",
    "chars": 12952,
    "preview": "PD94bWwgdmVyc2lvbj0iMS4wIj8+CjxzYW1sMnA6UmVzcG9uc2UgeG1sbnM6c2FtbDJwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wi"
  },
  {
    "path": "providertests/testdata/onelogin/olgn09_response_48.b64",
    "chars": 16964,
    "preview": "PD94bWwgdmVyc2lvbj0iMS4wIj8+CjxzYW1sMnA6UmVzcG9uc2UgeG1sbnM6c2FtbDJwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wi"
  },
  {
    "path": "providertests/testdata/onelogin/olgn09_response_50.b64",
    "chars": 8152,
    "preview": "PD94bWwgdmVyc2lvbj0iMS4wIj8+CjxzYW1sMnA6UmVzcG9uc2UgeG1sbnM6c2FtbDJwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wi"
  },
  {
    "path": "providertests/testdata/onelogin/olgn09_response_51.b64",
    "chars": 8240,
    "preview": "PD94bWwgdmVyc2lvbj0iMS4wIj8+CjxzYW1sMnA6UmVzcG9uc2UgeG1sbnM6c2FtbDJwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wi"
  },
  {
    "path": "providertests/testdata/onelogin/olgn09_response_52.b64",
    "chars": 8240,
    "preview": "PD94bWwgdmVyc2lvbj0iMS4wIj8+CjxzYW1sMnA6UmVzcG9uc2UgeG1sbnM6c2FtbDJwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wi"
  },
  {
    "path": "providertests/testdata/onelogin/olgn09_response_53.b64",
    "chars": 8240,
    "preview": "PD94bWwgdmVyc2lvbj0iMS4wIj8+CjxzYW1sMnA6UmVzcG9uc2UgeG1sbnM6c2FtbDJwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wi"
  },
  {
    "path": "providertests/testdata/onelogin/olgn09_response_54.b64",
    "chars": 8240,
    "preview": "PD94bWwgdmVyc2lvbj0iMS4wIj8+CjxzYW1sMnA6UmVzcG9uc2UgeG1sbnM6c2FtbDJwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wi"
  },
  {
    "path": "providertests/testdata/onelogin/olgn09_response_55.b64",
    "chars": 8120,
    "preview": "PHNhbWwycDpSZXNwb25zZSB4bWxuczpzYW1sMnA9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpwcm90b2NvbCIgSUQ9ImlkNzA5MzgyOTUzNzUwMjI1"
  },
  {
    "path": "providertests/testdata/onelogin/olgn09_response_56.b64",
    "chars": 8208,
    "preview": "PHNhbWwycDpSZXNwb25zZSB4bWxuczpzYW1sMnA9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpwcm90b2NvbCIgRGVzdGluYXRpb249Imh0dHBzOi8v"
  },
  {
    "path": "providertests/testdata/onelogin/olgn09_response_57.b64",
    "chars": 8208,
    "preview": "PHNhbWwycDpSZXNwb25zZSB4bWxuczpzYW1sMnA9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpwcm90b2NvbCIgRGVzdGluYXRpb249Imh0dHBzOi8v"
  },
  {
    "path": "providertests/testdata/onelogin/olgn09_response_58.b64",
    "chars": 8208,
    "preview": "PHNhbWwycDpSZXNwb25zZSB4bWxuczpzYW1sMnA9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpwcm90b2NvbCIgRGVzdGluYXRpb249Imh0dHBzOi8v"
  },
  {
    "path": "providertests/testdata/onelogin/olgn09_response_59.b64",
    "chars": 8208,
    "preview": "PHNhbWwycDpSZXNwb25zZSB4bWxuczpzYW1sMnA9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpwcm90b2NvbCIgRGVzdGluYXRpb249Imh0dHBzOi8v"
  },
  {
    "path": "providertests/testdata/onelogin/olgn09_response_81.b64",
    "chars": 6432,
    "preview": "PHNhbWwycDpSZXNwb25zZSB4bWxuczpzYW1sMnA9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIu\nMDpwcm90b2NvbCIKICAgICAgICAgICAgICAgICBEZXN"
  },
  {
    "path": "providertests/testdata/onelogin/olgn09_response_82.b64",
    "chars": 6432,
    "preview": "PHNhbWwycDpSZXNwb25zZSB4bWxuczpzYW1sMnA9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIu\nMDpwcm90b2NvbCIKICAgICAgICAgICAgICAgICBEZXN"
  },
  {
    "path": "providertests/testdata/onelogin/olgn09_response_83.b64",
    "chars": 8184,
    "preview": "PHNhbWwycDpSZXNwb25zZSB4bWxuczpzYW1sMnA9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpwcm90b2NvbCIgRGVzdGluYXRpb249Imh0dHBzOi8v"
  },
  {
    "path": "providertests/testdata/onelogin/olgn09_response_84.b64",
    "chars": 8184,
    "preview": "PHNhbWwycDpSZXNwb25zZSB4bWxuczpzYW1sMnA9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpwcm90b2NvbCIgRGVzdGluYXRpb249Imh0dHBzOi8v"
  },
  {
    "path": "providertests/testdata/onelogin/olgn09_response_85.b64",
    "chars": 6264,
    "preview": "PHNhbWwycDpSZXNwb25zZSB4bWxuczpzYW1sMnA9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpwcm90b2NvbCIKICAgICAgICAgICAgICAgICBJRD0i"
  },
  {
    "path": "providertests/testdata/onelogin/olgn09_response_86.b64",
    "chars": 6376,
    "preview": "PHNhbWwycDpSZXNwb25zZSB4bWxuczpzYW1sMnA9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpwcm90b2NvbCIKICAgICAgICAgICAgICAgICBEZXN0"
  },
  {
    "path": "providertests/testdata/onelogin/olgn09_response_87.b64",
    "chars": 6376,
    "preview": "PHNhbWwycDpSZXNwb25zZSB4bWxuczpzYW1sMnA9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpwcm90b2NvbCIKICAgICAgICAgICAgICAgICBEZXN0"
  },
  {
    "path": "providertests/testdata/onelogin/olgn09_response_88.b64",
    "chars": 6376,
    "preview": "PHNhbWwycDpSZXNwb25zZSB4bWxuczpzYW1sMnA9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpwcm90b2NvbCIKICAgICAgICAgICAgICAgICBEZXN0"
  },
  {
    "path": "providertests/testdata/onelogin/olgn09_response_89.b64",
    "chars": 6376,
    "preview": "PHNhbWwycDpSZXNwb25zZSB4bWxuczpzYW1sMnA9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpwcm90b2NvbCIKICAgICAgICAgICAgICAgICBEZXN0"
  },
  {
    "path": "providertests/testdata/onelogin/olgn09_response_91.b64",
    "chars": 6432,
    "preview": "PHNhbWwycDpSZXNwb25zZSB4bWxuczpzYW1sMnA9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIu\nMDpwcm90b2NvbCIKICAgICAgICAgICAgICAgICBEZXN"
  },
  {
    "path": "providertests/testdata/onelogin/olgn09_response_92.b64",
    "chars": 6432,
    "preview": "PHNhbWwycDpSZXNwb25zZSB4bWxuczpzYW1sMnA9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIu\nMDpwcm90b2NvbCIKICAgICAgICAgICAgICAgICBEZXN"
  },
  {
    "path": "providertests/testdata/onelogin/olgn09_response_93.b64",
    "chars": 8184,
    "preview": "PHNhbWwycDpSZXNwb25zZSB4bWxuczpzYW1sMnA9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpwcm90b2NvbCIgRGVzdGluYXRpb249Imh0dHBzOi8v"
  },
  {
    "path": "providertests/testdata/onelogin/olgn09_response_94.b64",
    "chars": 8184,
    "preview": "PHNhbWwycDpSZXNwb25zZSB4bWxuczpzYW1sMnA9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpwcm90b2NvbCIgRGVzdGluYXRpb249Imh0dHBzOi8v"
  },
  {
    "path": "providertests/testdata/onelogin/olgn09_response_97.b64",
    "chars": 10048,
    "preview": "PHNhbWwycDpSZXNwb25zZSB4bWxuczpzYW1sMnA9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpwcm90b2NvbCIKICAgICAgICAgICAgICAgICBEZXN0"
  },
  {
    "path": "providertests/testdata/onelogin/olgn09_response_98.b64",
    "chars": 10048,
    "preview": "PHNhbWwycDpSZXNwb25zZSB4bWxuczpzYW1sMnA9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpwcm90b2NvbCIKICAgICAgICAgICAgICAgICBEZXN0"
  },
  {
    "path": "providertests/testdata/onelogin/olgn09_response_99.b64",
    "chars": 6460,
    "preview": "PHNhbWwycDpSZXNwb25zZSB4bWxuczpzYW1sMnA9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIu\nMDpwcm90b2NvbCIKICAgICAgICAgICAgICAgICBEZXN"
  },
  {
    "path": "providertests/testdata/onelogin/sp.encryption.cert",
    "chars": 1184,
    "preview": "-----BEGIN CERTIFICATE-----\nMIIDPjCCAiagAwIBAgIJAOvpZtJNtQ/rMA0GCSqGSIb3DQEBCwUAMFUxCzAJBgNV\nBAYTAlVTMRMwEQYDVQQIEwpDYWx"
  },
  {
    "path": "providertests/testdata/onelogin/sp.encryption.key",
    "chars": 1704,
    "preview": "-----BEGIN PRIVATE KEY-----\nMIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCn1JXIsAzwifSV\n8tTbMSn6sjny0FxSihoDSikS8yw"
  },
  {
    "path": "providertests/testdata/onelogin/sp.signing.cert",
    "chars": 1184,
    "preview": "-----BEGIN CERTIFICATE-----\nMIIDPjCCAiagAwIBAgIJAKHPtM2MqZxXMA0GCSqGSIb3DQEBCwUAMFUxCzAJBgNV\nBAYTAlVTMRMwEQYDVQQIEwpDYWx"
  },
  {
    "path": "providertests/testdata/onelogin/sp.signing.key",
    "chars": 1704,
    "preview": "-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC1sPBD/3UhtMsu\n3C9rEL3Eaok67FQls0OUhoGTRmx"
  },
  {
    "path": "providertests/testdata/onelogin_cert.pem",
    "chars": 1501,
    "preview": "-----BEGIN CERTIFICATE-----\nMIIEJjCCAw6gAwIBAgIUOHrykO4ce1TbjvGgXXVVnR4NsqMwDQYJKoZIhvcNAQEF\nBQAwXTELMAkGA1UEBhMCVVMxFTA"
  },
  {
    "path": "providertests/testdata/onelogin_response.xml",
    "chars": 4301,
    "preview": "<samlp:Response xmlns:saml=\"urn:oasis:names:tc:SAML:2.0:assertion\" xmlns:samlp=\"urn:oasis:names:tc:SAML:2.0:protocol\" ID"
  },
  {
    "path": "providertests/testdata/pingfed/idp.signing.cert",
    "chars": 1184,
    "preview": "-----BEGIN CERTIFICATE-----\nMIIDPjCCAiagAwIBAgIJAMQeWnDJVaTyMA0GCSqGSIb3DQEBCwUAMFUxCzAJBgNV\nBAYTAlVTMRMwEQYDVQQIEwpDYWx"
  },
  {
    "path": "providertests/testdata/pingfed/pfed11_response_01.b64",
    "chars": 4917,
    "preview": "PHNhbWxwOlJlc3BvbnNlIFZlcnNpb249IjIuMCIgSUQ9Ik95RHVjOFZPX1Y2UGZlLUMuNHlYQzJoS0xRMCIgSXNzdWVJbnN0YW50PSIyMDE3LTA5LTAyVDAw"
  },
  {
    "path": "providertests/testdata/pingfed/pfed11_response_02.b64",
    "chars": 6457,
    "preview": "PHNhbWxwOlJlc3BvbnNlIFZlcnNpb249IjIuMCIgSUQ9Im9vTWpNTUpMTTl2RDZPSTRGbG4xejh5X3lodSIgSXNzdWVJbnN0YW50PSIyMDE3LTA5LTAyVDIz"
  },
  {
    "path": "providertests/testdata/pingfed/pfed11_response_03.b64",
    "chars": 4825,
    "preview": "PHNhbWxwOlJlc3BvbnNlIFZlcnNpb249IjIuMCIgSUQ9IlZfbGJUdjVhbUJOM1dSeFU1c2ZDLl9RT0F1ViIgSXNzdWVJbnN0YW50PSIyMDE3LTA5LTAxVDIx"
  },
  {
    "path": "providertests/testdata/pingfed/pfed11_response_05.b64",
    "chars": 13261,
    "preview": "PHNhbWxwOlJlc3BvbnNlIFZlcnNpb249IjIuMCIgSUQ9IlZlRHIxRlRIZDZtdEZUSmlmUFN1SDRZSlRQRiIgSXNzdWVJbnN0YW50PSIyMDE3LTA5LTAzVDAx"
  },
  {
    "path": "providertests/testdata/pingfed/sp.encryption.cert",
    "chars": 1184,
    "preview": "-----BEGIN CERTIFICATE-----\nMIIDPjCCAiagAwIBAgIJAOvpZtJNtQ/rMA0GCSqGSIb3DQEBCwUAMFUxCzAJBgNV\nBAYTAlVTMRMwEQYDVQQIEwpDYWx"
  },
  {
    "path": "providertests/testdata/pingfed/sp.encryption.key",
    "chars": 1704,
    "preview": "-----BEGIN PRIVATE KEY-----\nMIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCn1JXIsAzwifSV\n8tTbMSn6sjny0FxSihoDSikS8yw"
  },
  {
    "path": "providertests/testdata/pingfed/sp.signing.cert",
    "chars": 1184,
    "preview": "-----BEGIN CERTIFICATE-----\nMIIDPjCCAiagAwIBAgIJAKHPtM2MqZxXMA0GCSqGSIb3DQEBCwUAMFUxCzAJBgNV\nBAYTAlVTMRMwEQYDVQQIEwpDYWx"
  },
  {
    "path": "providertests/testdata/pingfed/sp.signing.key",
    "chars": 1704,
    "preview": "-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC1sPBD/3UhtMsu\n3C9rEL3Eaok67FQls0OUhoGTRmx"
  },
  {
    "path": "providertests/testdata/saml.oktadev.com/oktadev.pem",
    "chars": 1183,
    "preview": "-----BEGIN CERTIFICATE-----\nMIIDPDCCAiQCCQDydJgOlszqbzANBgkqhkiG9w0BAQUFADBgMQswCQYDVQQGEwJV\nUzETMBEGA1UECBMKQ2FsaWZvcm5"
  },
  {
    "path": "providertests/testdata/saml.oktadev.com/response_0",
    "chars": 5796,
    "preview": "PHNhbWxwOlJlc3BvbnNlIHhtbG5zOnNhbWxwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wiIElEPSJfZDczMWM3YTQ2NGMwODZmMDJj"
  },
  {
    "path": "providertests/testdata/saml.oktadev.com/response_1",
    "chars": 2920,
    "preview": "PHNhbWxwOlJlc3BvbnNlIHhtbG5zOnNhbWxwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wiIElEPSJfYjUyYjhhMjdhMmUwMWJiYTIy"
  },
  {
    "path": "providertests/testdata/saml.oktadev.com/response_10",
    "chars": 5768,
    "preview": "PHNhbWxwOlJlc3BvbnNlIHhtbG5zOnNhbWxwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wiIElEPSJfZTNlYjNjODQ4NjFhY2JjYjhi"
  },
  {
    "path": "providertests/testdata/saml.oktadev.com/response_11",
    "chars": 5868,
    "preview": "PHNhbWxwOlJlc3BvbnNlIHhtbG5zOnNhbWxwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wiIElEPSJfNWUxNWJkYWI0OGZlNzFlODc4"
  },
  {
    "path": "providertests/testdata/saml.oktadev.com/response_12",
    "chars": 2864,
    "preview": "PHNhbWxwOlJlc3BvbnNlIHhtbG5zOnNhbWxwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wiIElEPSJfZGNiZDcyZmEwZjc2M2YyNWEw"
  },
  {
    "path": "providertests/testdata/saml.oktadev.com/response_13",
    "chars": 5792,
    "preview": "PHNhbWxwOlJlc3BvbnNlIHhtbG5zOnNhbWxwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wiIElEPSJfZTZhMWFlZjE1N2ViMzQ0YjQz"
  },
  {
    "path": "providertests/testdata/saml.oktadev.com/response_14",
    "chars": 5824,
    "preview": "PHNhbWxwOlJlc3BvbnNlIHhtbG5zOnNhbWxwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wiIElEPSJfNzNlNTQzY2FkMjJkZGI0YWJj"
  },
  {
    "path": "providertests/testdata/saml.oktadev.com/response_15",
    "chars": 5872,
    "preview": "PHNhbWxwOlJlc3BvbnNlIHhtbG5zOnNhbWxwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wiIElEPSJfNGNhNTMwMTMzZWRiYzExZDc4"
  },
  {
    "path": "providertests/testdata/saml.oktadev.com/response_16",
    "chars": 2688,
    "preview": "xVhZc6rKFv4rlvvRSgARFStJXSYVARXBAV92MbTMNDLLr7+gSU6SSnKy9324VT50r/Hr1aubr31I9TCIJxuQxjBKQacKgyidXIWP3TyJJlBP3XQS6SFIJ5k5"
  },
  {
    "path": "providertests/testdata/saml.oktadev.com/response_2",
    "chars": 5868,
    "preview": "PHNhbWxwOlJlc3BvbnNlIHhtbG5zOnNhbWxwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wiIElEPSJfMDg2Y2ZjMWVlMGJkYThhMDAz"
  },
  {
    "path": "providertests/testdata/saml.oktadev.com/response_3",
    "chars": 6084,
    "preview": "PHNhbWxwOlJlc3BvbnNlIHhtbG5zOnNhbWxwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wiIElEPSJfNjEzOGY0ODBjODRlNmUzZGZl"
  },
  {
    "path": "providertests/testdata/saml.oktadev.com/response_4",
    "chars": 5848,
    "preview": "PHNhbWxwOlJlc3BvbnNlIHhtbG5zOnNhbWxwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wiIElEPSJfOGM1ZjQ0NDMyNmMwZjM3N2Yz"
  },
  {
    "path": "providertests/testdata/saml.oktadev.com/response_5",
    "chars": 5848,
    "preview": "PHNhbWxwOlJlc3BvbnNlIHhtbG5zOnNhbWxwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wiIElEPSJfODE0ZDRiYmNlMThmODQ4ODNj"
  },
  {
    "path": "providertests/testdata/saml.oktadev.com/response_6",
    "chars": 5860,
    "preview": "PHNhbWxwOlJlc3BvbnNlIHhtbG5zOnNhbWxwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wiIElEPSJfMzFmMmM3NmY0ZDhlNTRmZTRm"
  },
  {
    "path": "providertests/testdata/saml.oktadev.com/response_7",
    "chars": 5636,
    "preview": "PHNhbWxwOlJlc3BvbnNlIHhtbG5zOnNhbWxwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wiIElEPSJfZThhMTdkMWYyNzEwM2Y1OGQ1"
  },
  {
    "path": "providertests/testdata/saml.oktadev.com/response_8",
    "chars": 5816,
    "preview": "PHNhbWxwOlJlc3BvbnNlIHhtbG5zOnNhbWxwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wiIElEPSJfN2I4ZmUwMWNmNjhhNTczMjBi"
  },
  {
    "path": "providertests/testdata/saml.oktadev.com/response_9",
    "chars": 5816,
    "preview": "PHNhbWxwOlJlc3BvbnNlIHhtbG5zOnNhbWxwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wiIElEPSJfMmQ4MDM0NmQyZmU4MGEzZjdj"
  },
  {
    "path": "providertests/utils.go",
    "chars": 4783,
    "preview": "// Copyright 2016 Russell Haering et al.\n// \n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you m"
  },
  {
    "path": "retrieve_assertion.go",
    "chars": 3293,
    "preview": "// Copyright 2016 Russell Haering et al.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you ma"
  },
  {
    "path": "run_test.sh",
    "chars": 243,
    "preview": "#!/bin/bash\ncd `dirname $0`\nDIRS=`git grep -l 'func Test' | xargs dirname | sort -u`\nfor DIR in $DIRS\ndo\n\techo\n\techo \"di"
  },
  {
    "path": "s2example/demo.go",
    "chars": 3531,
    "preview": "// Copyright 2016 Russell Haering et al.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you ma"
  },
  {
    "path": "saml.go",
    "chars": 12484,
    "preview": "// Copyright 2016 Russell Haering et al.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you ma"
  },
  {
    "path": "saml_test.go",
    "chars": 15409,
    "preview": "// Copyright 2016 Russell Haering et al.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you ma"
  },
  {
    "path": "test_constants.go",
    "chars": 134659,
    "preview": "// Copyright 2016 Russell Haering et al.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you ma"
  },
  {
    "path": "testdata/assertion.xml",
    "chars": 2398,
    "preview": "<saml2:Assertion xmlns:saml2=\"urn:oasis:names:tc:SAML:2.0:assertion\" ID=\"_e665af85bf88de41ef9886c0718ecdd0\" IssueInstant"
  },
  {
    "path": "testdata/idp.crt",
    "chars": 1176,
    "preview": "-----BEGIN CERTIFICATE-----\nMIIDODCCAiCgAwIBAgIUDPz+OwougAXSuQmKDyAEL46KlPgwDQYJKoZIhvcNAQEL\nBQAwHTEbMBkGA1UEAwwSY29sbGV"
  },
  {
    "path": "testdata/saml.post",
    "chars": 19353,
    "preview": "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHNhbWwycDpSZXNwb25zZSBEZXN0aW5hdGlvbj0iaHR0cHM6Ly9zYW1sMi50ZXN0LmFz"
  },
  {
    "path": "testdata/saml.xml",
    "chars": 6484,
    "preview": "<saml2:Assertion xmlns:saml2=\"urn:oasis:names:tc:SAML:2.0:assertion\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" ID=\"_c"
  },
  {
    "path": "testdata/saml_compressed.post",
    "chars": 2721,
    "preview": "zVhbd6q6Gv0rDtejo+Um3kbbcbgqKiICKr6cwSXKPQhBkV+/QVt329O1dtc65+G8xS9fZmZmvoQZn3IrjtLRCuQpTHLQKuMoyUfX4HO7yJIRtHI/HyVWDPIR"
  },
  {
    "path": "testdata/test.crt",
    "chars": 1399,
    "preview": "-----BEGIN CERTIFICATE-----\nMIID2zCCAsOgAwIBAgIJANfD2hyjW72lMA0GCSqGSIb3DQEBCwUAMIGDMQswCQYD\nVQQGEwJaWjEUMBIGA1UECAwLVHJ"
  },
  {
    "path": "testdata/test.key",
    "chars": 1704,
    "preview": "-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCnC2D3V8abs1kE\nBKsB0BKYrzo2yHV80WzqnPgUV8L"
  },
  {
    "path": "types/encrypted_assertion.go",
    "chars": 3808,
    "preview": "// Copyright 2016 Russell Haering et al.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you ma"
  },
  {
    "path": "types/encrypted_key.go",
    "chars": 6271,
    "preview": "// Copyright 2016 Russell Haering et al.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you ma"
  },
  {
    "path": "types/metadata.go",
    "chars": 4170,
    "preview": "// Copyright 2016 Russell Haering et al.\n// \n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you m"
  },
  {
    "path": "types/response.go",
    "chars": 7669,
    "preview": "// Copyright 2016 Russell Haering et al.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you ma"
  },
  {
    "path": "uuid/uuid.go",
    "chars": 1069,
    "preview": "// Copyright 2016 Russell Haering et al.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you ma"
  },
  {
    "path": "uuid/uuid_test.go",
    "chars": 980,
    "preview": "// Copyright 2016 Russell Haering et al.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you ma"
  },
  {
    "path": "validate.go",
    "chars": 8092,
    "preview": "// Copyright 2016 Russell Haering et al.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you ma"
  },
  {
    "path": "xml_constants.go",
    "chars": 2914,
    "preview": "// Copyright 2016 Russell Haering et al.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you ma"
  }
]

About this extraction

This page contains the full source code of the russellhaering/gosaml2 GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 166 files (1.2 MB), approximately 706.9k tokens, and a symbol index with 264 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!