wai-saml2 0.4 → 0.5
raw patch · 11 files changed
+349/−11 lines, 11 filesdep ~bytestringdep ~textdep ~zlib
Dependency ranges changed: bytestring, text, zlib
Files
- CHANGELOG.md +8/−0
- src/Network/Wai/SAML2/C14N.hs +6/−3
- src/Network/Wai/SAML2/Request.hs +10/−2
- src/Network/Wai/SAML2/Response.hs +13/−0
- src/Network/Wai/SAML2/Validation.hs +6/−3
- src/Network/Wai/SAML2/XML.hs +18/−1
- tests/data/metadata/google.xml +29/−0
- tests/data/metadata/google.xml.expected +139/−0
- tests/data/metadata/keycloak.xml +25/−0
- tests/data/metadata/keycloak.xml.expected +89/−0
- wai-saml2.cabal +6/−2
CHANGELOG.md view
@@ -1,5 +1,13 @@ # Changelog for `wai-saml2` +## Unreleased changes++## 0.5++- Support GHC 9.6 ([#53](https://github.com/mbg/wai-saml2/pull/53) by [@mbg](https://github.com/mbg))+- Fixed a bug in XML canonicalisation causing a digest mismatch on Okta when assertion attributes are present (special thanks to @hiroqn) ([#51](https://github.com/mbg/wai-saml2/pull/51) by [@fumieval](https://github.com/fumieval))+- Added `authnRequestDestination` field to `AuthnRequest` ([#47](https://github.com/mbg/wai-saml2/pull/47) by [@Philonous](https://github.com/Philonous))+ ## 0.4 - Split `validateResponse` into `decodeResponse` and `validateSAMLResponse` ([#31](https://github.com/mbg/wai-saml2/pull/31) by [@fumieval](https://github.com/fumieval))
src/Network/Wai/SAML2/C14N.hs view
@@ -14,6 +14,8 @@ -------------------------------------------------------------------------------- import qualified Data.ByteString as BS+import Data.Text (Text)+import qualified Data.Text.Encoding as T import Foreign.C.Types @@ -21,9 +23,10 @@ -------------------------------------------------------------------------------- --- | 'canonicalise' @xml@ produces a canonical representation of @xml@.-canonicalise :: BS.ByteString -> IO BS.ByteString-canonicalise xml = c14n c14nOpts c14n_exclusive_1_0 [] False Nothing xml+-- | 'canonicalise' @prefixList@ @xml@ produces a canonical representation of @xml@+-- while retaining namespaces matching @prefixList@.+canonicalise :: [Text] -> BS.ByteString -> IO BS.ByteString+canonicalise prefixList xml = c14n c14nOpts c14n_exclusive_1_0 (map T.encodeUtf8 prefixList) False Nothing xml -- | The options we want to use for canonicalisation of XML documents. c14nOpts :: [CInt]
src/Network/Wai/SAML2/Request.hs view
@@ -63,6 +63,11 @@ , authnRequestID :: !T.Text -- | SP Entity ID , authnRequestIssuer :: !T.Text+ -- | The URI reference to which this request is to be sent. Required+ -- for signed requests+ --+ -- @since 0.5+ , authnRequestDestination :: !(Maybe T.Text) -- | Allow IdP to generate a new identifier , authnRequestAllowCreate :: !Bool -- | The URI reference corresponding to a name identifier format@@ -83,6 +88,7 @@ pure AuthnRequest{ authnRequestAllowCreate = True , authnRequestNameIDFormat = Transient+ , authnRequestDestination = Nothing , .. } @@ -118,13 +124,15 @@ root = Element (saml2pName "AuthnRequest") (Map.fromList- [ ("xmlns:samlp", "urn:oasis:names:tc:SAML:2.0:protocol")+ ([ ("xmlns:samlp", "urn:oasis:names:tc:SAML:2.0:protocol") , ("xmlns:saml", "urn:oasis:names:tc:SAML:2.0:assertion") , ("ID", authnRequestID) -- Reference [RequestAbstractType] and see [ID Values] , ("Version", "2.0") -- [RequestAbstractType] , ("IssueInstant", timestamp) -- [RequestAbstractType] , ("AssertionConsumerServiceIndex", "1") -- [AuthnRequest]- ])+ ]+ -- [RequestAbstractType]+ ++ [("Destination", uri) | let Just uri = authnRequestDestination] )) [NodeElement issuer, NodeElement nameIdPolicy] -- Reference [RequestAbstractType] issuer = Element
src/Network/Wai/SAML2/Response.hs view
@@ -11,6 +11,7 @@ Response(..), removeSignature, extractSignedInfo,+ extractPrefixList, -- * Re-exports module Network.Wai.SAML2.StatusCode,@@ -132,6 +133,18 @@ &/ element (dsName "SignedInfo") ) >>= nodes pure signedInfo++-- | Obtain a list of InclusiveNamespaces entries used for exclusive XML canonicalisation.+--+-- @since 0.5+extractPrefixList :: Cursor -> [T.Text]+extractPrefixList cursor = concatMap T.words+ $ concatMap (attribute "PrefixList")+ $ cursor+ $/ element (dsName "Reference")+ &/ element (dsName "Transforms")+ &/ element (dsName "Transform")+ &/ element (ecName "InclusiveNamespaces") --------------------------------------------------------------------------------
src/Network/Wai/SAML2/Validation.hs view
@@ -16,7 +16,9 @@ -------------------------------------------------------------------------------- import Control.Exception+import Control.Monad (forM_, when, unless) import Control.Monad.Except+import Control.Monad.IO.Class (liftIO) import Crypto.Error import Crypto.Hash@@ -69,7 +71,7 @@ -- try to parse the XML document; throw an exception if it is not -- a valid XML document- responseXmlDoc <- case XML.parseLBS def (LBS.fromStrict resXmlDocData) of+ responseXmlDoc <- case XML.parseLBS parseSettings (LBS.fromStrict resXmlDocData) of Left err -> throwError $ InvalidResponseXml err Right responseXmlDoc -> pure responseXmlDoc @@ -130,8 +132,9 @@ let signedInfoXml = XML.renderLBS def doc -- canonicalise the textual representation of the SignedInfo element+ let prefixList = extractPrefixList (XML.fromDocument doc) signedInfoCanonResult <- liftIO $ try $- canonicalise (LBS.toStrict signedInfoXml)+ canonicalise prefixList (LBS.toStrict signedInfoXml) normalisedSignedInfo <- case signedInfoCanonResult of Left err -> throwError $ CanonicalisationFailure err@@ -161,7 +164,7 @@ -- then render the resulting document and canonicalise it let renderedXml = XML.renderLBS def docMinusSignature- refCanonResult <- liftIO $ try $ canonicalise (LBS.toStrict renderedXml)+ refCanonResult <- liftIO $ try $ canonicalise prefixList (LBS.toStrict renderedXml) normalised <- case refCanonResult of Left err -> throwError $ CanonicalisationFailure err
src/Network/Wai/SAML2/XML.hs view
@@ -13,6 +13,7 @@ xencName, dsName, mdName,+ ecName, -- * Utility functions toMaybeText,@@ -21,7 +22,8 @@ -- * XML parsing FromXML(..),- oneOrFail+ oneOrFail,+ parseSettings ) where --------------------------------------------------------------------------------@@ -66,6 +68,14 @@ Name name (Just "urn:oasis:names:tc:SAML:2.0:metadata") (Just "md") +-- | 'ecName' @name@ constructs a 'Name' for @name@ in the+-- http://www.w3.org/2001/10/xml-exc-c14n# namespace.+--+-- @since 0.5+ecName :: T.Text -> Name+ecName name =+ Name name (Just "http://www.w3.org/2001/10/xml-exc-c14n#") (Just "ec")+ -- | 'toMaybeText' @xs@ returns 'Nothing' if @xs@ is the empty list, or -- the result of concatenating @xs@ wrapped in 'Just' otherwise. toMaybeText :: [T.Text] -> Maybe T.Text@@ -100,3 +110,10 @@ oneOrFail _ (x:_) = pure x --------------------------------------------------------------------------------++-- | It is important to retain namespaces in order to calculate the hash of the canonicalised XML correctly.+-- see: https://stackoverflow.com/questions/69252831/saml-2-0-digest-value-calculation-in-saml-assertion+--+-- @since 0.5+parseSettings :: ParseSettings+parseSettings = def { psRetainNamespaces = True }
+ tests/data/metadata/google.xml view
@@ -0,0 +1,29 @@+<?xml version="1.0" encoding="UTF-8"?><md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata" entityID="https://accounts.google.com/o/saml2?idpid=C01aa60hc" validUntil="2027-07-20T09:18:00.000Z">+ <md:IDPSSODescriptor WantAuthnRequestsSigned="false" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">+ <md:KeyDescriptor use="signing">+ <ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">+ <ds:X509Data>+ <ds:X509Certificate>MIIDdDCCAlygAwIBAgIGAYIgDLo9MA0GCSqGSIb3DQEBCwUAMHsxFDASBgNVBAoTC0dvb2dsZSBJ+bmMuMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MQ8wDQYDVQQDEwZHb29nbGUxGDAWBgNVBAsTD0dv+b2dsZSBGb3IgV29yazELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWEwHhcNMjIwNzIx+MDkxODAwWhcNMjcwNzIwMDkxODAwWjB7MRQwEgYDVQQKEwtHb29nbGUgSW5jLjEWMBQGA1UEBxMN+TW91bnRhaW4gVmlldzEPMA0GA1UEAxMGR29vZ2xlMRgwFgYDVQQLEw9Hb29nbGUgRm9yIFdvcmsx+CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A+MIIBCgKCAQEA9KQNdYuN7oPUsJ1SK2L58egBELYIWr5cRd9PXuV1k1s2hzt2vMoPS1l788+/hVb4+CtZCeabZDFYfNMbS2olbhG06FrS9+ptm2pKucXqEp+KM9FozmK/qn7+geQl7uHdcT0xRqfVK46Wr+RB4S8VdbK9WIK2prCvHIPu1O9eH+m9g+G1Y116RIqKKGfGbsWdhyNY47+LcxXzThgdfi4BPwcB/i+K/X/i4qNje4FLHrAJQ7S6bRiTXPyv7q9Md96R1T9hXdFA9siW9rmrJCg/H022TrqxwctlIkQjPGj+yskF3vCcwuIj5M8PuP37k6AuiBZdV73gG8t8SgoMHhElWSLKowIDAQABMA0GCSqGSIb3DQEBCwUA+A4IBAQDuIthaqITon7u3aZg70NutC54EsQ6NLfWsbwgadOahb5EtGnBkKB7bairnTSAMtH4wLnNj+BrgxswnxVd5DdrmqpuoY+Pn3hAXYBCFwmAzEEYwmfkZbTVa7AcT6bw6nOqHrfnYump9LhUwxI4dV+4Yr2YkLL767CeSMLv58GBlFAqxP63HVMa1BJAhZhjamqvSYQCJZYnBG7Nuy2ek6Y5kHQM/8L0h6n+gx1Jv0nPvPI+9NlWRkUhDN/jQkzxKcYnCNlT/bi3xY+QjFWD4z/Qx2h9hrxZtLRtcrp6WqUmpXha+dq1JsmSbXwRj4Tu7IVZ4tYqeY/mOJGYf8fr5h/Q8GRCd</ds:X509Certificate>+ </ds:X509Data>+ </ds:KeyInfo>+ </md:KeyDescriptor>+ <md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress</md:NameIDFormat>+ <md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://accounts.google.com/o/saml2/idp?idpid=C01aa60hc"/>+ <md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://accounts.google.com/o/saml2/idp?idpid=C01aa60hc"/>+ </md:IDPSSODescriptor>+</md:EntityDescriptor>
+ tests/data/metadata/google.xml.expected view
@@ -0,0 +1,139 @@+IDPSSODescriptor+ { entityID = "https://accounts.google.com/o/saml2?idpid=C01aa60hc"+ , x509Certificate =+ SignedExact+ { getSigned =+ Signed+ { signedObject =+ Certificate+ { certVersion = 2+ , certSerial = 1658395081277+ , certSignatureAlg = SignatureALG HashSHA256 PubKeyALG_RSA+ , certIssuerDN =+ DistinguishedName+ { getDistinguishedElements =+ [ ( [ 2 , 5 , 4 , 10 ]+ , ASN1CharacterString+ { characterEncoding = Printable+ , getCharacterStringRawData = "Google Inc."+ }+ )+ , ( [ 2 , 5 , 4 , 7 ]+ , ASN1CharacterString+ { characterEncoding = Printable+ , getCharacterStringRawData = "Mountain View"+ }+ )+ , ( [ 2 , 5 , 4 , 3 ]+ , ASN1CharacterString+ { characterEncoding = Printable+ , getCharacterStringRawData = "Google"+ }+ )+ , ( [ 2 , 5 , 4 , 11 ]+ , ASN1CharacterString+ { characterEncoding = Printable+ , getCharacterStringRawData = "Google For Work"+ }+ )+ , ( [ 2 , 5 , 4 , 6 ]+ , ASN1CharacterString+ { characterEncoding = Printable+ , getCharacterStringRawData = "US"+ }+ )+ , ( [ 2 , 5 , 4 , 8 ]+ , ASN1CharacterString+ { characterEncoding = Printable+ , getCharacterStringRawData = "California"+ }+ )+ ]+ }+ , certValidity =+ ( DateTime+ { dtDate =+ Date { dateYear = 2022 , dateMonth = July , dateDay = 21 }+ , dtTime =+ TimeOfDay+ { todHour = 9 h , todMin = 18 m , todSec = 0 s , todNSec = 0 ns }+ }+ , DateTime+ { dtDate =+ Date { dateYear = 2027 , dateMonth = July , dateDay = 20 }+ , dtTime =+ TimeOfDay+ { todHour = 9 h , todMin = 18 m , todSec = 0 s , todNSec = 0 ns }+ }+ )+ , certSubjectDN =+ DistinguishedName+ { getDistinguishedElements =+ [ ( [ 2 , 5 , 4 , 10 ]+ , ASN1CharacterString+ { characterEncoding = Printable+ , getCharacterStringRawData = "Google Inc."+ }+ )+ , ( [ 2 , 5 , 4 , 7 ]+ , ASN1CharacterString+ { characterEncoding = Printable+ , getCharacterStringRawData = "Mountain View"+ }+ )+ , ( [ 2 , 5 , 4 , 3 ]+ , ASN1CharacterString+ { characterEncoding = Printable+ , getCharacterStringRawData = "Google"+ }+ )+ , ( [ 2 , 5 , 4 , 11 ]+ , ASN1CharacterString+ { characterEncoding = Printable+ , getCharacterStringRawData = "Google For Work"+ }+ )+ , ( [ 2 , 5 , 4 , 6 ]+ , ASN1CharacterString+ { characterEncoding = Printable+ , getCharacterStringRawData = "US"+ }+ )+ , ( [ 2 , 5 , 4 , 8 ]+ , ASN1CharacterString+ { characterEncoding = Printable+ , getCharacterStringRawData = "California"+ }+ )+ ]+ }+ , certPubKey =+ PubKeyRSA+ PublicKey+ { public_size = 256+ , public_n =+ 30883043751460212091009024715988672157201981026347726370499687818828606623610036063558065564910721575436562697595833895389078194628597753679300253553943785088191301337694287320462812204176082900979493095532403911400962461718281343090255859587662209446117641661982570706673532506066365285856542563187402675519172527708567282278848705855498817485769832020293045021199187525818891914876547302505893509597998904610940749090045911784327818251262543295743496580502018384973717126996683932142133838721711519234780192895538524052074852409104614561380839223725538723547399956034683375536115322508017066877882514789596315962019+ , public_e = 65537+ }+ , certExtensions = Extensions Nothing+ }+ , signedAlg = SignatureALG HashSHA256 PubKeyALG_RSA+ , signedSignature =+ "\238\"\216Z\168\132\232\159\187\183i\152;\208\219\173\v\158\EOT\177\SO\141-\245\172o\b\SUBt\230\161o\145-\SUBpd(\RS\219j*\231M \f\180~0.sc\ACK\184\&1\179\t\241U\222Cv\185\170\166\234\CAN\248\249\247\132\ENQ\216\EOT!p\152\f\196\DC1\140&~F[MV\187\SOH\196\250o\SO\167:\161\235~v.\154\159K\133L1#\135U\225\138\246bB\203\239\174\194y#\v\191\159\ACK\ACKQ@\171\DC3\250\220uLkPI\STX\SYNa\141\169\170\189&\DLE\b\150X\156\DC1\187\&6\236\182zN\152\230A\208\&3\255\v\210\RS\167\131\GSI\191I\207\188\242>\244\217VFE!\f\223\227BL\241)\198'\b\217S\253\184\183\197\143\144\140U\131\227?\208\199h}\134\188Y\180\180mr\186zZ\165&\165xZv\173I\178d\155_\EOTc\225;\187!Vx\181\138\158c\249\142$f\US\241\250\249\135\244<\EM\DLE\157"+ }+ , exactObjectRaw =+ "0\130\STX\\\160\ETX\STX\SOH\STX\STX\ACK\SOH\130 \f\186=0\r\ACK\t*\134H\134\247\r\SOH\SOH\v\ENQ\NUL0{1\DC40\DC2\ACK\ETXU\EOT\n\DC3\vGoogle Inc.1\SYN0\DC4\ACK\ETXU\EOT\a\DC3\rMountain View1\SI0\r\ACK\ETXU\EOT\ETX\DC3\ACKGoogle1\CAN0\SYN\ACK\ETXU\EOT\v\DC3\SIGoogle For Work1\v0\t\ACK\ETXU\EOT\ACK\DC3\STXUS1\DC30\DC1\ACK\ETXU\EOT\b\DC3\nCalifornia0\RS\ETB\r220721091800Z\ETB\r270720091800Z0{1\DC40\DC2\ACK\ETXU\EOT\n\DC3\vGoogle Inc.1\SYN0\DC4\ACK\ETXU\EOT\a\DC3\rMountain View1\SI0\r\ACK\ETXU\EOT\ETX\DC3\ACKGoogle1\CAN0\SYN\ACK\ETXU\EOT\v\DC3\SIGoogle For Work1\v0\t\ACK\ETXU\EOT\ACK\DC3\STXUS1\DC30\DC1\ACK\ETXU\EOT\b\DC3\nCalifornia0\130\SOH\"0\r\ACK\t*\134H\134\247\r\SOH\SOH\SOH\ENQ\NUL\ETX\130\SOH\SI\NUL0\130\SOH\n\STX\130\SOH\SOH\NUL\244\164\ru\139\141\238\131\212\176\157R+b\249\241\232\SOH\DLE\182\bZ\190\\E\223O^\229u\147[6\135;v\188\202\SIKY{\243\207\191\133V\248\n\214By\166\217\fV\US4\198\210\218\137[\132m:\SYN\180\189\250\155f\218\146\174qz\132\167\226\140\244Z3\152\175\234\159\191\160y\t{\184w\\OLQ\169\245J\227\165\171D\RS\DC2\241W[+\213\136+jk\n\241\200>\237N\245\225\254\155\216>\ESCV5\215\164H\168\162\134|f\236Y\216r5\142;\248\183\&1_4\225\129\215\226\224\DC3\240p\US\226+\245\255\139\138\141\141\238\ENQ,z\192%\SO\210\233\180bMs\242\191\186\189\&1\223zGT\253\133wE\ETX\219\"[\218\230\172\144\160\252}6\217:\234\199\a-\148\137\DLE\140\241\163\202\201\ENQ\222\240\156\194\226#\228\207\SI\184\253\251\147\160.\136\SYN]W\189\224\ESC\203|J\n\f\RS\DC1%Y\"\202\163\STX\ETX\SOH\NUL\SOH"+ , encodeSignedObject =+ "0\130\ETXt0\130\STX\\\160\ETX\STX\SOH\STX\STX\ACK\SOH\130 \f\186=0\r\ACK\t*\134H\134\247\r\SOH\SOH\v\ENQ\NUL0{1\DC40\DC2\ACK\ETXU\EOT\n\DC3\vGoogle Inc.1\SYN0\DC4\ACK\ETXU\EOT\a\DC3\rMountain View1\SI0\r\ACK\ETXU\EOT\ETX\DC3\ACKGoogle1\CAN0\SYN\ACK\ETXU\EOT\v\DC3\SIGoogle For Work1\v0\t\ACK\ETXU\EOT\ACK\DC3\STXUS1\DC30\DC1\ACK\ETXU\EOT\b\DC3\nCalifornia0\RS\ETB\r220721091800Z\ETB\r270720091800Z0{1\DC40\DC2\ACK\ETXU\EOT\n\DC3\vGoogle Inc.1\SYN0\DC4\ACK\ETXU\EOT\a\DC3\rMountain View1\SI0\r\ACK\ETXU\EOT\ETX\DC3\ACKGoogle1\CAN0\SYN\ACK\ETXU\EOT\v\DC3\SIGoogle For Work1\v0\t\ACK\ETXU\EOT\ACK\DC3\STXUS1\DC30\DC1\ACK\ETXU\EOT\b\DC3\nCalifornia0\130\SOH\"0\r\ACK\t*\134H\134\247\r\SOH\SOH\SOH\ENQ\NUL\ETX\130\SOH\SI\NUL0\130\SOH\n\STX\130\SOH\SOH\NUL\244\164\ru\139\141\238\131\212\176\157R+b\249\241\232\SOH\DLE\182\bZ\190\\E\223O^\229u\147[6\135;v\188\202\SIKY{\243\207\191\133V\248\n\214By\166\217\fV\US4\198\210\218\137[\132m:\SYN\180\189\250\155f\218\146\174qz\132\167\226\140\244Z3\152\175\234\159\191\160y\t{\184w\\OLQ\169\245J\227\165\171D\RS\DC2\241W[+\213\136+jk\n\241\200>\237N\245\225\254\155\216>\ESCV5\215\164H\168\162\134|f\236Y\216r5\142;\248\183\&1_4\225\129\215\226\224\DC3\240p\US\226+\245\255\139\138\141\141\238\ENQ,z\192%\SO\210\233\180bMs\242\191\186\189\&1\223zGT\253\133wE\ETX\219\"[\218\230\172\144\160\252}6\217:\234\199\a-\148\137\DLE\140\241\163\202\201\ENQ\222\240\156\194\226#\228\207\SI\184\253\251\147\160.\136\SYN]W\189\224\ESC\203|J\n\f\RS\DC1%Y\"\202\163\STX\ETX\SOH\NUL\SOH0\r\ACK\t*\134H\134\247\r\SOH\SOH\v\ENQ\NUL\ETX\130\SOH\SOH\NUL\238\"\216Z\168\132\232\159\187\183i\152;\208\219\173\v\158\EOT\177\SO\141-\245\172o\b\SUBt\230\161o\145-\SUBpd(\RS\219j*\231M \f\180~0.sc\ACK\184\&1\179\t\241U\222Cv\185\170\166\234\CAN\248\249\247\132\ENQ\216\EOT!p\152\f\196\DC1\140&~F[MV\187\SOH\196\250o\SO\167:\161\235~v.\154\159K\133L1#\135U\225\138\246bB\203\239\174\194y#\v\191\159\ACK\ACKQ@\171\DC3\250\220uLkPI\STX\SYNa\141\169\170\189&\DLE\b\150X\156\DC1\187\&6\236\182zN\152\230A\208\&3\255\v\210\RS\167\131\GSI\191I\207\188\242>\244\217VFE!\f\223\227BL\241)\198'\b\217S\253\184\183\197\143\144\140U\131\227?\208\199h}\134\188Y\180\180mr\186zZ\165&\165xZv\173I\178d\155_\EOTc\225;\187!Vx\181\138\158c\249\142$f\US\241\250\249\135\244<\EM\DLE\157"+ }+ , nameIDFormats =+ [ "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress" ]+ , singleSignOnServices =+ [ ( HTTPRedirect+ , "https://accounts.google.com/o/saml2/idp?idpid=C01aa60hc"+ )+ , ( HTTPPost+ , "https://accounts.google.com/o/saml2/idp?idpid=C01aa60hc"+ )+ ]+ }
+ tests/data/metadata/keycloak.xml view
@@ -0,0 +1,25 @@+<?xml version="1.0" encoding="UTF-8"?>+<md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata" xmlns="urn:oasis:names:tc:SAML:2.0:metadata" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" entityID="https://sso.ja-sore.de/auth/realms/HERP">+ <md:IDPSSODescriptor WantAuthnRequestsSigned="true" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">+ <md:KeyDescriptor use="signing">+ <ds:KeyInfo>+ <ds:KeyName>AViu9Cf7_rjRHwqkm_MgvMcjWHuRPSP0mMNNEjHslaY</ds:KeyName>+ <ds:X509Data>+ <ds:X509Certificate>MIIClzCCAX8CBgF3TK79BzANBgkqhkiG9w0BAQsFADAPMQ0wCwYDVQQDDARIRVJQMB4XDTIxMDEyOTA1NDYxMloXDTMxMDEyOTA1NDc1MlowDzENMAsGA1UEAwwESEVSUDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANRBHQMqF2+g7CrESyzrv+HvwjvIdhdZWWc6z4GiD3zN1TE+cbh88JMUrVTWd7EJLq+9uAazCyZfzX5BdauDqsp+LP9z2xiJ6Zlao1z5ycfG4dwvEIlOv4rX1zSdtg35Cz+QdaUfIkCEX3dTHI7dFcwou+Nel0BGCnp17w8gVPOBGYzvOtBgWcNV9+9Zmn7oSRoG5R4uRzhmPv12pyKRO/0hZr6gTkQ11CsnyfW890bDM05I5m8gDT0fAO2PznbQIQKClqUoyNbJZbBeDyV7QaEkkC8Nckvo6y+bae8oEF9pxQiXBV7dHtY8VLWq5SN2IeX1VK59hMg2NdHUxjMZIiECAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAzGn04cz8oaZoRUCo6OKhcwfOSrjojfqbUxW8ZAY6UPUV8IfAbaxMlc4j1NVq3awi+yCJWLDaZyOX4mQyD+NoHB5D1/qQi/wLBkg9TJt2wp6MjkOPrwkN8lvhabq8Us2NiBnhHzLjWawltie9r1W0pugRG4qSZvJqosJJh9Wr0m8MnaP28P7fp4J/j3UAivlGZvXXh1kcl/lRWhoYqmR3r2j98OWy/WdYfx/oPNDWMdJDdR9I/qtlOVHsiEDP8Z478/z9IYweOYYqg7Ou77Ys/5G6wRxRCwaUjMDPdFfyvfUBZ9EFpYUV1G/zXuGQwfuh27GWz8gytdd1vDxBU06h3w==</ds:X509Certificate>+ </ds:X509Data>+ </ds:KeyInfo>+ </md:KeyDescriptor>+ <md:ArtifactResolutionService Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP" Location="https://sso.ja-sore.de/auth/realms/HERP/protocol/saml/resolve" index="0" />+ <md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://sso.ja-sore.de/auth/realms/HERP/protocol/saml" />+ <md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://sso.ja-sore.de/auth/realms/HERP/protocol/saml" />+ <md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact" Location="https://sso.ja-sore.de/auth/realms/HERP/protocol/saml" />+ <md:NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:persistent</md:NameIDFormat>+ <md:NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:transient</md:NameIDFormat>+ <md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified</md:NameIDFormat>+ <md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress</md:NameIDFormat>+ <md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://sso.ja-sore.de/auth/realms/HERP/protocol/saml" />+ <md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://sso.ja-sore.de/auth/realms/HERP/protocol/saml" />+ <md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP" Location="https://sso.ja-sore.de/auth/realms/HERP/protocol/saml" />+ <md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact" Location="https://sso.ja-sore.de/auth/realms/HERP/protocol/saml" />+ </md:IDPSSODescriptor>+</md:EntityDescriptor>
+ tests/data/metadata/keycloak.xml.expected view
@@ -0,0 +1,89 @@+IDPSSODescriptor+ { entityID = "https://sso.ja-sore.de/auth/realms/HERP"+ , x509Certificate =+ SignedExact+ { getSigned =+ Signed+ { signedObject =+ Certificate+ { certVersion = 0+ , certSerial = 1611899272455+ , certSignatureAlg = SignatureALG HashSHA256 PubKeyALG_RSA+ , certIssuerDN =+ DistinguishedName+ { getDistinguishedElements =+ [ ( [ 2 , 5 , 4 , 3 ]+ , ASN1CharacterString+ { characterEncoding = UTF8+ , getCharacterStringRawData = "HERP"+ }+ )+ ]+ }+ , certValidity =+ ( DateTime+ { dtDate =+ Date { dateYear = 2021 , dateMonth = January , dateDay = 29 }+ , dtTime =+ TimeOfDay+ { todHour = 5 h , todMin = 46 m , todSec = 12 s , todNSec = 0 ns }+ }+ , DateTime+ { dtDate =+ Date { dateYear = 2031 , dateMonth = January , dateDay = 29 }+ , dtTime =+ TimeOfDay+ { todHour = 5 h , todMin = 47 m , todSec = 52 s , todNSec = 0 ns }+ }+ )+ , certSubjectDN =+ DistinguishedName+ { getDistinguishedElements =+ [ ( [ 2 , 5 , 4 , 3 ]+ , ASN1CharacterString+ { characterEncoding = UTF8+ , getCharacterStringRawData = "HERP"+ }+ )+ ]+ }+ , certPubKey =+ PubKeyRSA+ PublicKey+ { public_size = 256+ , public_n =+ 26794629232301390559329251866062548351573676959105948670047115599484903764153173211872085488745203643849524181464124430648664560428395941550339690556064181777927415101879096439027796505139145202723504898654935482393722978364774771646139306434335595822802292206982564879345724045673514452796802353368762193429772204243864488315862304970769841689431483551389268024017379114959044330977741690398490550580701831871711628065022359440646669330356587937408829251700791982308190330679272353052846282673680829331107723412188694305482396334605009242290197499924909025290277992294341810791131897406148044363907966374411188642337+ , public_e = 65537+ }+ , certExtensions = Extensions Nothing+ }+ , signedAlg = SignatureALG HashSHA256 PubKeyALG_RSA+ , signedSignature =+ "\204i\244\225\204\252\161\166hE@\168\232\226\161s\a\206J\184\232\141\250\155S\NAK\188d\ACK:P\245\NAK\240\135\192m\172L\149\206#\212\213j\221\172\"\251 \137X\176\218g#\151\226d2\SI\227h\FS\RSC\215\250\144\139\252\v\ACKH=L\155v\194\158\140\142C\143\175\t\r\242[\225i\186\188R\205\141\136\EM\225\US2\227Y\172%\182'\189\175U\180\166\232\DC1\ESC\138\146f\242j\162\194I\135\213\171\210o\f\157\163\246\240\254\223\167\130\DEL\143u\NUL\138\249Ff\245\215\135Y\FS\151\249QZ\SUB\CAN\170dw\175h\253\240\229\178\253gX\DEL\US\232<\208\214\&1\210Cu\USH\254\171e9Q\236\136@\207\241\158;\243\252\253!\140\RS9\134*\131\179\174\239\182,\255\145\186\193\FSQ\v\ACK\148\140\192\207tW\242\189\245\SOHg\209\ENQ\165\133\NAK\212o\243^\225\144\193\251\161\219\177\150\207\200\&2\181\215u\188<ASN\161\223"+ }+ , exactObjectRaw =+ "0\130\SOH\DEL\STX\ACK\SOHwL\174\253\a0\r\ACK\t*\134H\134\247\r\SOH\SOH\v\ENQ\NUL0\SI1\r0\v\ACK\ETXU\EOT\ETX\f\EOTHERP0\RS\ETB\r210129054612Z\ETB\r310129054752Z0\SI1\r0\v\ACK\ETXU\EOT\ETX\f\EOTHERP0\130\SOH\"0\r\ACK\t*\134H\134\247\r\SOH\SOH\SOH\ENQ\NUL\ETX\130\SOH\SI\NUL0\130\SOH\n\STX\130\SOH\SOH\NUL\212A\GS\ETX*\ETBo\160\236*\196K,\235\191\225\239\194;\200v\ETBYYg:\207\129\162\SI|\205\213\&1>q\184|\240\147\DC4\173T\214w\177\t.\175\189\184\ACK\179\v&_\205~Au\171\131\170\202~,\255s\219\CAN\137\233\153Z\163\\\249\201\199\198\225\220/\DLE\137N\191\138\215\215\&4\157\182\r\249\v?\144u\165\US\"@\132_wS\FS\142\221\NAK\204(\187\227^\151@F\nzu\239\SI T\243\129\EM\140\239:\208`Y\195U\247\239Y\154~\232I\SUB\ACK\229\RS.G8f>\253v\167\"\145;\253!f\190\160ND5\212+'\201\245\188\247F\195\&3NH\230o \r=\US\NUL\237\143\206v\208!\STX\130\150\165(\200\214\201e\176^\SI%{A\161$\144/\rrK\232\235/\155i\239(\DLE_i\197\b\151\ENQ^\221\RS\214<T\181\170\229#v!\229\245T\174}\132\200\&65\209\212\198\&3\EM\"!\STX\ETX\SOH\NUL\SOH"+ , encodeSignedObject =+ "0\130\STX\151\&0\130\SOH\DEL\STX\ACK\SOHwL\174\253\a0\r\ACK\t*\134H\134\247\r\SOH\SOH\v\ENQ\NUL0\SI1\r0\v\ACK\ETXU\EOT\ETX\f\EOTHERP0\RS\ETB\r210129054612Z\ETB\r310129054752Z0\SI1\r0\v\ACK\ETXU\EOT\ETX\f\EOTHERP0\130\SOH\"0\r\ACK\t*\134H\134\247\r\SOH\SOH\SOH\ENQ\NUL\ETX\130\SOH\SI\NUL0\130\SOH\n\STX\130\SOH\SOH\NUL\212A\GS\ETX*\ETBo\160\236*\196K,\235\191\225\239\194;\200v\ETBYYg:\207\129\162\SI|\205\213\&1>q\184|\240\147\DC4\173T\214w\177\t.\175\189\184\ACK\179\v&_\205~Au\171\131\170\202~,\255s\219\CAN\137\233\153Z\163\\\249\201\199\198\225\220/\DLE\137N\191\138\215\215\&4\157\182\r\249\v?\144u\165\US\"@\132_wS\FS\142\221\NAK\204(\187\227^\151@F\nzu\239\SI T\243\129\EM\140\239:\208`Y\195U\247\239Y\154~\232I\SUB\ACK\229\RS.G8f>\253v\167\"\145;\253!f\190\160ND5\212+'\201\245\188\247F\195\&3NH\230o \r=\US\NUL\237\143\206v\208!\STX\130\150\165(\200\214\201e\176^\SI%{A\161$\144/\rrK\232\235/\155i\239(\DLE_i\197\b\151\ENQ^\221\RS\214<T\181\170\229#v!\229\245T\174}\132\200\&65\209\212\198\&3\EM\"!\STX\ETX\SOH\NUL\SOH0\r\ACK\t*\134H\134\247\r\SOH\SOH\v\ENQ\NUL\ETX\130\SOH\SOH\NUL\204i\244\225\204\252\161\166hE@\168\232\226\161s\a\206J\184\232\141\250\155S\NAK\188d\ACK:P\245\NAK\240\135\192m\172L\149\206#\212\213j\221\172\"\251 \137X\176\218g#\151\226d2\SI\227h\FS\RSC\215\250\144\139\252\v\ACKH=L\155v\194\158\140\142C\143\175\t\r\242[\225i\186\188R\205\141\136\EM\225\US2\227Y\172%\182'\189\175U\180\166\232\DC1\ESC\138\146f\242j\162\194I\135\213\171\210o\f\157\163\246\240\254\223\167\130\DEL\143u\NUL\138\249Ff\245\215\135Y\FS\151\249QZ\SUB\CAN\170dw\175h\253\240\229\178\253gX\DEL\US\232<\208\214\&1\210Cu\USH\254\171e9Q\236\136@\207\241\158;\243\252\253!\140\RS9\134*\131\179\174\239\182,\255\145\186\193\FSQ\v\ACK\148\140\192\207tW\242\189\245\SOHg\209\ENQ\165\133\NAK\212o\243^\225\144\193\251\161\219\177\150\207\200\&2\181\215u\188<ASN\161\223"+ }+ , nameIDFormats =+ [ "urn:oasis:names:tc:SAML:2.0:nameid-format:persistent"+ , "urn:oasis:names:tc:SAML:2.0:nameid-format:transient"+ , "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"+ , "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"+ ]+ , singleSignOnServices =+ [ ( HTTPPost+ , "https://sso.ja-sore.de/auth/realms/HERP/protocol/saml"+ )+ , ( HTTPRedirect+ , "https://sso.ja-sore.de/auth/realms/HERP/protocol/saml"+ )+ , ( SOAP+ , "https://sso.ja-sore.de/auth/realms/HERP/protocol/saml"+ )+ , ( HTTPArtifact+ , "https://sso.ja-sore.de/auth/realms/HERP/protocol/saml"+ )+ ]+ }
wai-saml2.cabal view
@@ -1,11 +1,11 @@ cabal-version: 1.12 --- This file has been generated from package.yaml by hpack version 0.35.1.+-- This file has been generated from package.yaml by hpack version 0.35.2. -- -- see: https://github.com/sol/hpack name: wai-saml2-version: 0.4+version: 0.5 synopsis: SAML2 assertion validation as WAI middleware description: A Haskell library which implements SAML2 assertion validation as WAI middleware category: Security@@ -26,6 +26,10 @@ tests/data/keycloak.xml.expected tests/data/okta.xml tests/data/okta.xml.expected+ tests/data/metadata/google.xml+ tests/data/metadata/google.xml.expected+ tests/data/metadata/keycloak.xml+ tests/data/metadata/keycloak.xml.expected source-repository head type: git