Wednesday, 19 March 2025

crewja saml manual sp and idp

 

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