1919using FoxIDs . SampleHelperLibrary . Models ;
2020using ITfoxtec . Identity . Util ;
2121using ITfoxtec . Identity . Saml2 . Claims ;
22+ using System . Web ;
23+ using Microsoft . AspNetCore . Mvc . Rendering ;
24+ using ITfoxtec . Identity ;
2225
2326namespace AspNetCoreSamlIdPSample . Controllers
2427{
@@ -48,30 +51,30 @@ public IActionResult Metadata()
4851 entityDescriptor . ValidUntil = 365 ;
4952 entityDescriptor . IdPSsoDescriptor = new IdPSsoDescriptor
5053 {
51- SigningCertificates = new X509Certificate2 [ ]
52- {
54+ SigningCertificates =
55+ [
5356 saml2Config . SigningCertificate
54- } ,
55- //EncryptionCertificates = new X509Certificate2[]
56- //{
57+ ] ,
58+ //EncryptionCertificates =
59+ //[
5760 // saml2Config.DecryptionCertificate
58- //} ,
59- SingleSignOnServices = new SingleSignOnService [ ]
60- {
61+ //] ,
62+ SingleSignOnServices =
63+ [
6164 new SingleSignOnService { Binding = ProtocolBindings . HttpRedirect , Location = new Uri ( UrlCombine . Combine ( defaultSite , "/Saml/Login" ) ) }
62- } ,
63- SingleLogoutServices = new SingleLogoutService [ ]
64- {
65+ ] ,
66+ SingleLogoutServices =
67+ [
6568 new SingleLogoutService { Binding = ProtocolBindings . HttpPost , Location = new Uri ( UrlCombine . Combine ( defaultSite , "/Saml/Logout" ) ) }
66- } ,
67- NameIDFormats = new Uri [ ] { NameIdentifierFormats . X509SubjectName } ,
69+ ] ,
70+ NameIDFormats = [ NameIdentifierFormats . X509SubjectName ] ,
6871 } ;
69- entityDescriptor . ContactPersons = new [ ] {
72+ entityDescriptor . ContactPersons = [
7073 new ContactPerson ( ContactTypes . Administrative )
7174 {
7275 Company = "Some sample IdP" ,
7376 }
74- } ;
77+ ] ;
7578 return new Saml2Metadata ( entityDescriptor ) . CreateMetadata ( ) . ToActionResult ( ) ;
7679 }
7780
@@ -86,26 +89,9 @@ public async Task<IActionResult> Login()
8689 {
8790 httpRequest . Binding . Unbind ( httpRequest , saml2AuthnRequest ) ;
8891
89- // **** Handle user login e.g. in GUI ****
90- // Test user with session index and claims
91- var session = await idPSessionCookieRepository . GetAsync ( ) ;
92- if ( session == null )
93- {
94- session = new IdPSession
95- {
96- RelyingPartyIssuer = relyingParty . Issuer ,
97- NameIdentifier = "12345" ,
98- Upn = "12345@email.test" ,
99- Email = "some@email.test" ,
100- CustomId = "123abc" ,
101- CustomName = "Test Users Custom Full Name" ,
102- SessionIndex = Guid . NewGuid ( ) . ToString ( )
103- } ;
104- await idPSessionCookieRepository . SaveAsync ( session ) ;
105- }
106- var claims = GetClaims ( session ) ;
107-
108- return LoginResponse ( saml2AuthnRequest . Id , Saml2StatusCodes . Success , httpRequest . Binding . RelayState , relyingParty , session . SessionIndex , claims ) ;
92+ var session = await GetSession ( relyingParty ) ;
93+
94+ return LoginResponse ( saml2AuthnRequest . Id , Saml2StatusCodes . Success , httpRequest . Binding . RelayState , relyingParty , session . SessionIndex , GetClaims ( session ) ) ;
10995 }
11096 catch ( Exception ex )
11197 {
@@ -114,20 +100,78 @@ public async Task<IActionResult> Login()
114100 }
115101 }
116102
117- private Saml2Configuration GetLoginSaml2Config ( RelyingParty relyingParty )
103+ [ Route ( "IdPInitiated" ) ]
104+ public IActionResult IdPInitiated ( )
118105 {
119- var loginSaml2Config = new Saml2Configuration
106+ return base . View ( new IdPInitiatedViewModel { RelyingPartyIssuers = GetRelyingPartyListItems ( ) } ) ;
107+ }
108+
109+ [ HttpPost ( "IdPInitiated" ) ]
110+ public async Task < IActionResult > IdPInitiated ( IdPInitiatedViewModel idPInitiatedViewModel )
111+ {
112+ if ( "oidc" . Equals ( idPInitiatedViewModel . ApplicationType , StringComparison . OrdinalIgnoreCase ) && idPInitiatedViewModel . ApplicationRedirectURL . IsNullOrWhiteSpace ( ) )
120113 {
121- Issuer = saml2Config . Issuer ,
122- SigningCertificate = saml2Config . SigningCertificate ,
123- SignatureAlgorithm = saml2Config . SignatureAlgorithm ,
124- CertificateValidationMode = saml2Config . CertificateValidationMode ,
125- RevocationMode = saml2Config . RevocationMode
126- } ;
127- loginSaml2Config . AllowedAudienceUris . AddRange ( saml2Config . AllowedAudienceUris ) ;
128- loginSaml2Config . EncryptionCertificate = relyingParty . EncryptionCertificate ;
114+ ModelState . AddModelError ( nameof ( idPInitiatedViewModel . ApplicationRedirectURL ) , $ "The { nameof ( idPInitiatedViewModel . ApplicationRedirectURL ) } field is required for OpenID Connect (oidc)") ;
115+ }
129116
130- return loginSaml2Config ;
117+ if ( ! ModelState . IsValid )
118+ {
119+ idPInitiatedViewModel . RelyingPartyIssuers = GetRelyingPartyListItems ( ) ;
120+ return View ( idPInitiatedViewModel ) ;
121+ }
122+
123+ var relyingParty = ValidateRelyingParty ( idPInitiatedViewModel . RelyingPartyIssuer ) ;
124+
125+ var binding = new Saml2PostBinding ( ) ;
126+ binding . RelayState = string . Join ( '&' , GetRelayState ( idPInitiatedViewModel ) ) ;
127+
128+ var response = new Saml2AuthnResponse ( GetLoginSaml2Config ( relyingParty ) ) ;
129+ response . Status = Saml2StatusCodes . Success ;
130+
131+ var session = await GetSession ( relyingParty ) ;
132+
133+ return LoginResponse ( null , Saml2StatusCodes . Success , binding . RelayState , relyingParty , session . SessionIndex , GetClaims ( session ) ) ;
134+
135+
136+ //var claimsIdentity = new ClaimsIdentity(GetClaims(session));
137+ //response.NameId = new Saml2NameIdentifier(claimsIdentity.Claims.Where(c => c.Type == ClaimTypes.NameIdentifier).Select(c => c.Value).Single(), NameIdentifierFormats.Persistent);
138+ //response.ClaimsIdentity = claimsIdentity;
139+ //var token = response.CreateSecurityToken(relyingParty.Issuer);
140+
141+ //return binding.Bind(response).ToActionResult();
142+ }
143+
144+ private IEnumerable < string > GetRelayState ( IdPInitiatedViewModel idPInitiatedViewModel )
145+ {
146+ yield return $ "app_name={ HttpUtility . UrlEncode ( idPInitiatedViewModel . ApplicationName . ToLower ( ) ) } ";
147+ yield return $ "app_type={ idPInitiatedViewModel . ApplicationType . ToLower ( ) } ";
148+ if ( ! idPInitiatedViewModel . ApplicationRedirectURL . IsNullOrWhiteSpace ( ) )
149+ {
150+ yield return $ "app_redirect={ HttpUtility . UrlEncode ( idPInitiatedViewModel . ApplicationRedirectURL ) } ";
151+ }
152+ }
153+
154+ private async Task < IdPSession > GetSession ( RelyingParty relyingParty )
155+ {
156+ // **** Handle user login e.g. in GUI ****
157+ // Test user with session index and claims
158+ var session = await idPSessionCookieRepository . GetAsync ( ) ;
159+ if ( session == null )
160+ {
161+ session = new IdPSession
162+ {
163+ RelyingPartyIssuer = relyingParty . Issuer ,
164+ NameIdentifier = "12345" ,
165+ Upn = "12345@email.test" ,
166+ Email = "some@email.test" ,
167+ CustomId = "123abc" ,
168+ CustomName = "Test Users Custom Full Name" ,
169+ SessionIndex = Guid . NewGuid ( ) . ToString ( )
170+ } ;
171+ await idPSessionCookieRepository . SaveAsync ( session ) ;
172+ }
173+
174+ return session ;
131175 }
132176
133177 private IEnumerable < Claim > GetClaims ( IdPSession idPSession )
@@ -145,6 +189,34 @@ private IEnumerable<Claim> GetClaims(IdPSession idPSession)
145189 yield return new Claim ( Saml2ClaimTypes . SessionIndex , idPSession . SessionIndex ) ;
146190 }
147191
192+ private IEnumerable < SelectListItem > GetRelyingPartyListItems ( ) => settings . RelyingParties . Select ( r => new SelectListItem ( r . SingleSignOnDestination . OriginalString , r . Issuer ) ) ;
193+
194+ private IActionResult LoginResponse ( Saml2Id inResponseTo , Saml2StatusCodes status , string relayState , RelyingParty relyingParty , string sessionIndex = null , IEnumerable < Claim > claims = null )
195+ {
196+ var responsebinding = new Saml2PostBinding ( ) ;
197+ responsebinding . RelayState = relayState ;
198+
199+ var saml2AuthnResponse = new Saml2AuthnResponse ( GetLoginSaml2Config ( relyingParty ) )
200+ {
201+ InResponseTo = inResponseTo ,
202+ Status = status ,
203+ Destination = relyingParty . SingleSignOnDestination ,
204+ } ;
205+ if ( status == Saml2StatusCodes . Success && claims != null )
206+ {
207+ saml2AuthnResponse . SessionIndex = sessionIndex ;
208+
209+ var claimsIdentity = new ClaimsIdentity ( claims ) ;
210+ saml2AuthnResponse . NameId = new Saml2NameIdentifier ( claimsIdentity . Claims . Where ( c => c . Type == ClaimTypes . NameIdentifier ) . Select ( c => c . Value ) . Single ( ) , NameIdentifierFormats . Persistent ) ;
211+ //saml2AuthnResponse.NameId = new Saml2NameIdentifier(claimsIdentity.Claims.Where(c => c.Type == ClaimTypes.NameIdentifier).Select(c => c.Value).Single());
212+ saml2AuthnResponse . ClaimsIdentity = claimsIdentity ;
213+
214+ _ = saml2AuthnResponse . CreateSecurityToken ( relyingParty . Issuer ) ;
215+ }
216+
217+ return responsebinding . Bind ( saml2AuthnResponse ) . ToActionResult ( ) ;
218+ }
219+
148220 [ Route ( "Logout" ) ]
149221 public async Task < IActionResult > Logout ( )
150222 {
@@ -222,46 +294,36 @@ private string ReadRelyingPartyFromLogoutResponse(Saml2Http.HttpRequest httpRequ
222294 return httpRequest . Binding . ReadSamlResponse ( httpRequest , new Saml2LogoutResponse ( saml2Config ) ) ? . Issuer ;
223295 }
224296
225- private IActionResult LoginResponse ( Saml2Id inResponseTo , Saml2StatusCodes status , string relayState , RelyingParty relyingParty , string sessionIndex = null , IEnumerable < Claim > claims = null )
297+ private IActionResult LogoutResponse ( Saml2Id inResponseTo , Saml2StatusCodes status , string relayState , string sessionIndex , RelyingParty relyingParty )
226298 {
227299 var responsebinding = new Saml2PostBinding ( ) ;
228300 responsebinding . RelayState = relayState ;
229301
230- var saml2AuthnResponse = new Saml2AuthnResponse ( GetLoginSaml2Config ( relyingParty ) )
302+ var saml2LogoutResponse = new Saml2LogoutResponse ( saml2Config )
231303 {
232304 InResponseTo = inResponseTo ,
233305 Status = status ,
234- Destination = relyingParty . SingleSignOnDestination ,
306+ Destination = relyingParty . SingleLogoutResponseDestination ,
307+ SessionIndex = sessionIndex
235308 } ;
236- if ( status == Saml2StatusCodes . Success && claims != null )
237- {
238- saml2AuthnResponse . SessionIndex = sessionIndex ;
239309
240- var claimsIdentity = new ClaimsIdentity ( claims ) ;
241- saml2AuthnResponse . NameId = new Saml2NameIdentifier ( claimsIdentity . Claims . Where ( c => c . Type == ClaimTypes . NameIdentifier ) . Select ( c => c . Value ) . Single ( ) , NameIdentifierFormats . Persistent ) ;
242- //saml2AuthnResponse.NameId = new Saml2NameIdentifier(claimsIdentity.Claims.Where(c => c.Type == ClaimTypes.NameIdentifier).Select(c => c.Value).Single());
243- saml2AuthnResponse . ClaimsIdentity = claimsIdentity ;
244-
245- _ = saml2AuthnResponse . CreateSecurityToken ( relyingParty . Issuer ) ;
246- }
247-
248- return responsebinding . Bind ( saml2AuthnResponse ) . ToActionResult ( ) ;
310+ return responsebinding . Bind ( saml2LogoutResponse ) . ToActionResult ( ) ;
249311 }
250312
251- private IActionResult LogoutResponse ( Saml2Id inResponseTo , Saml2StatusCodes status , string relayState , string sessionIndex , RelyingParty relyingParty )
313+ private Saml2Configuration GetLoginSaml2Config ( RelyingParty relyingParty )
252314 {
253- var responsebinding = new Saml2PostBinding ( ) ;
254- responsebinding . RelayState = relayState ;
255-
256- var saml2LogoutResponse = new Saml2LogoutResponse ( saml2Config )
315+ var loginSaml2Config = new Saml2Configuration
257316 {
258- InResponseTo = inResponseTo ,
259- Status = status ,
260- Destination = relyingParty . SingleLogoutResponseDestination ,
261- SessionIndex = sessionIndex
317+ Issuer = saml2Config . Issuer ,
318+ SigningCertificate = saml2Config . SigningCertificate ,
319+ SignatureAlgorithm = saml2Config . SignatureAlgorithm ,
320+ CertificateValidationMode = saml2Config . CertificateValidationMode ,
321+ RevocationMode = saml2Config . RevocationMode
262322 } ;
323+ loginSaml2Config . AllowedAudienceUris . AddRange ( saml2Config . AllowedAudienceUris ) ;
324+ loginSaml2Config . EncryptionCertificate = relyingParty . EncryptionCertificate ;
263325
264- return responsebinding . Bind ( saml2LogoutResponse ) . ToActionResult ( ) ;
326+ return loginSaml2Config ;
265327 }
266328
267329 private RelyingParty ValidateRelyingParty ( string issuer )
0 commit comments