Created
March 13, 2024 15:49
-
-
Save Apurer/6210fa88ee2614ee812aa626261aeb31 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"crypto/x509" | |
"fmt" | |
"net/http" | |
"io/ioutil" | |
"encoding/base64" | |
"encoding/xml" | |
saml2 "github.com/russellhaering/gosaml2" | |
"github.com/russellhaering/gosaml2/types" | |
dsig "github.com/russellhaering/goxmldsig" | |
) | |
func main() { | |
res, err := http.Get("https://login.microsoftonline.com/{TenantId}/federationmetadata/2007-06/federationmetadata.xml") | |
if err != nil { | |
panic(err) | |
} | |
rawMetadata, err := ioutil.ReadAll(res.Body) | |
if err != nil { | |
panic(err) | |
} | |
metadata := &types.EntityDescriptor{} | |
err = xml.Unmarshal(rawMetadata, metadata) | |
if err != nil { | |
panic(err) | |
} | |
certStore := dsig.MemoryX509CertificateStore{ | |
Roots: []*x509.Certificate{}, | |
} | |
for _, kd := range metadata.IDPSSODescriptor.KeyDescriptors { | |
for idx, xcert := range kd.KeyInfo.X509Data.X509Certificates { | |
if xcert.Data == "" { | |
panic(fmt.Errorf("metadata certificate(%d) must not be empty", idx)) | |
} | |
certData, err := base64.StdEncoding.DecodeString(xcert.Data) | |
if err != nil { | |
panic(err) | |
} | |
idpCert, err := x509.ParseCertificate(certData) | |
if err != nil { | |
panic(err) | |
} | |
certStore.Roots = append(certStore.Roots, idpCert) | |
} | |
} | |
// We sign the AuthnRequest with a random key because Okta doesn't seem | |
// to verify these. | |
randomKeyStore := dsig.RandomKeyStoreForTest() | |
sp := &saml2.SAMLServiceProvider{ | |
IdentityProviderSSOURL: metadata.IDPSSODescriptor.SingleSignOnServices[0].Location, | |
IdentityProviderIssuer: metadata.EntityID, | |
ServiceProviderIssuer: "http://your_service_provider_entity_id", // Your entity ID | |
AssertionConsumerServiceURL: "http://localhost:8080/v1/_saml_callback", // Your ACS URL | |
SignAuthnRequests: true, | |
AudienceURI: "http://your_service_provider_entity_id", // Your audience URI, typically the same as your entity ID | |
IDPCertificateStore: &certStore, | |
SPKeyStore: randomKeyStore, // Keep or adjust as necessary | |
} | |
http.HandleFunc("/v1/_saml_callback", func(rw http.ResponseWriter, req *http.Request) { | |
err := req.ParseForm() | |
if err != nil { | |
rw.WriteHeader(http.StatusBadRequest) | |
return | |
} | |
assertionInfo, err := sp.RetrieveAssertionInfo(req.FormValue("SAMLResponse")) | |
if err != nil { | |
rw.WriteHeader(http.StatusForbidden) | |
return | |
} | |
if assertionInfo.WarningInfo.InvalidTime { | |
rw.WriteHeader(http.StatusForbidden) | |
return | |
} | |
if assertionInfo.WarningInfo.NotInAudience { | |
rw.WriteHeader(http.StatusForbidden) | |
return | |
} | |
fmt.Fprintf(rw, "NameID: %s\n", assertionInfo.NameID) | |
fmt.Fprintf(rw, "Assertions:\n") | |
for key, val := range assertionInfo.Values { | |
fmt.Fprintf(rw, " %s: %+v\n", key, val) | |
} | |
fmt.Fprintf(rw, "\n") | |
fmt.Fprintf(rw, "Warnings:\n") | |
fmt.Fprintf(rw, "%+v\n", assertionInfo.WarningInfo) | |
}) | |
println("Visit this URL To Authenticate:") | |
authURL, err := sp.BuildAuthURL("") | |
if err != nil { | |
panic(err) | |
} | |
println(authURL) | |
println("Supply:") | |
fmt.Printf(" SP ACS URL : %s\n", sp.AssertionConsumerServiceURL) | |
err = http.ListenAndServe(":8080", nil) | |
if err != nil { | |
panic(err) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment