go get github.com/crewjam/saml/samlsp
the following might not be fully functional for gin refer to
https://github.com/crewjam/saml/issues/185
samlSP, _ = samlsp.New(samlsp.Options{
URL: *rootURL,
Key: keyPair.PrivateKey.(*rsa.PrivateKey),
Certificate: keyPair.Leaf,
IDPMetadataURL: idpMetadataURL,
})
router := gin.Default()
router.Any("/saml/*action", gin.WrapH(samlSP))
api := router.Group("/api")
api.Use(adapter.Wrap(samlSP.RequireAccount))
err := router.Run()
https://github.com/gwatts/gin-adapter
Also the saml subject from IDP is send and stored in session as jwt token with base64 encode, if you used requireAccount middleware, there are two ways to retrieve it
1.customized acs
2 samlSession, ok := session.(samlsp.JWTSessionClaims)
if !ok {
return
}
//String :{"nameID":"admin"}
c.JSON(http.StatusOK, gin.H{"nameID": samlSession.Subject})
package main
import (
"crypto/rsa"
"crypto/tls"
"crypto/x509"
"encoding/pem"
"log"
"net/http"
"net/url"
"github.com/crewjam/saml"
"github.com/crewjam/saml/samlsp"
)
// Load X.509 Certificate and Private Key
func loadCertificate(certPath, keyPath string) (*x509.Certificate, *rsa.PrivateKey, error) {
certPEM, err := tls.LoadX509KeyPair(certPath, keyPath)
if err != nil {
return nil, nil, err
}
certBlock, _ := pem.Decode(certPEM.Certificate[0])
if certBlock == nil {
return nil, nil, err
}
cert, err := x509.ParseCertificate(certBlock.Bytes)
if err != nil {
return nil, nil, err
}
key, ok := certPEM.PrivateKey.(*rsa.PrivateKey)
if !ok {
return nil, nil, err
}
return cert, key, nil
}
// Define IdP Metadata Manually
func getIDPMetadata() *saml.EntityDescriptor {
idpCertPEM := `-----BEGIN CERTIFICATE-----
MIIC+TCCAeGgAwIBAgIJAPoVnx9vAgxwMA0GCSqGSIb3DQEBCwUAMFcxCzAJBgNV
...
-----END CERTIFICATE-----`
return &saml.EntityDescriptor{
EntityID: "https://idp.example.com",
IDPSSODescriptor: &saml.IDPSSODescriptor{
SingleSignOnServices: []saml.Endpoint{
{
Binding: saml.HTTPRedirectBinding,
Location: "https://idp.example.com/sso/redirect",
},
{
Binding: saml.HTTPPostBinding,
Location: "https://idp.example.com/sso/post",
},
},
SingleLogoutServices: []saml.Endpoint{
{
Binding: saml.HTTPRedirectBinding,
Location: "https://idp.example.com/slo/redirect",
},
{
Binding: saml.HTTPPostBinding,
Location: "https://idp.example.com/slo/post",
},
},
KeyDescriptors: []saml.KeyDescriptor{
{
Use: "signing",
KeyInfo: saml.KeyInfo{
XMLName: saml.XMLName{Local: "ds:KeyInfo"},
X509Data: &saml.X509Data{X509Certificates: []string{idpCertPEM}},
},
},
},
},
}
}
func main() {
// Load SP certificate & key
cert, key, err := loadCertificate("sp-cert.pem", "sp-key.pem")
if err != nil {
log.Fatalf("Failed to load certificate: %v", err)
}
// Define SP Metadata
spEntityID := "https://myservice.example.com/metadata"
spACSURL := "https://myservice.example.com/saml/acs"
spSLOURL := "https://myservice.example.com/saml/logout"
sp := saml.ServiceProvider{
EntityID: spEntityID,
Key: key,
Certificate: cert,
AssertionConsumerServices: []saml.Endpoint{
{
Binding: saml.HTTPPostBinding,
Location: spACSURL,
},
{
Binding: saml.HTTPRedirectBinding,
Location: spACSURL,
},
},
SingleLogoutServices: []saml.Endpoint{
{
Binding: saml.HTTPPostBinding,
Location: spSLOURL,
},
{
Binding: saml.HTTPRedirectBinding,
Location: spSLOURL,
},
},
NameIDFormat: saml.EmailAddressNameIDFormat,
IDPMetadata: getIDPMetadata(),
}
// Configure SAML Middleware
samlMiddleware := samlsp.Middleware{
ServiceProvider: sp,
}
// Secure route
app := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
user, _ := samlsp.SessionFromContext(r.Context())
w.Write([]byte("Hello, " + user.(samlsp.Session).GetNameID() + "!"))
})
http.Handle("/hello", samlMiddleware.RequireAccount(app))
http.Handle("/saml/", samlMiddleware)
log.Println("Server started at :8000")
log.Fatal(http.ListenAndServe(":8000", nil))
}
No comments:
Post a Comment