Skip to content

Commit

Permalink
Initial checkin
Browse files Browse the repository at this point in the history
  • Loading branch information
yoelhor committed Mar 20, 2020
0 parents commit ace9702
Show file tree
Hide file tree
Showing 76 changed files with 24,882 additions and 0 deletions.
3 changes: 3 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Azure AD B2C SAML tester web app

For more information, see [Register a SAML application in Azure AD B2C](https://docs.microsoft.com/azure/active-directory-b2c/connect-with-saml-service-providers)
182 changes: 182 additions & 0 deletions source-code/B2CPolicyTemplate.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
<TrustFrameworkPolicy xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://schemas.microsoft.com/online/cpim/schemas/2013/06" PolicySchemaVersion="0.3.0.0" TenantId="%TENANTID%" PolicyId="%POLICYID%" PublicPolicyUri="http://%TENANTID%/">
<BuildingBlocks>
<!--
- Element: ClaimsSchema
- Details: Contains the list of Claims required for this User Journey
-->
<ClaimsSchema>
<ClaimType Id="objectId">
<DisplayName>User's Object ID</DisplayName>
<DataType>string</DataType>
<UserHelpText>Object identifier (ID) of the user object in Azure AD.</UserHelpText>
</ClaimType>
<ClaimType Id="signInName">
<DisplayName>Sign in name</DisplayName>
<DataType>string</DataType>
<UserHelpText/>
<UserInputType>TextBox</UserInputType>
</ClaimType>
<ClaimType Id="Title">
<DisplayName>Title</DisplayName>
<DataType>string</DataType>
<AdminHelpText />
<UserHelpText />
<UserInputType>TextBox</UserInputType>
</ClaimType>
<ClaimType Id="givenName">
<DisplayName>Given Name</DisplayName>
<DataType>string</DataType>
<UserHelpText>Your given name (also known as first name).</UserHelpText>
<UserInputType>TextBox</UserInputType>
</ClaimType>
<ClaimType Id="surname">
<DisplayName>Surname</DisplayName>
<DataType>string</DataType>
<UserHelpText>Your surname (also known as family name or last name).</UserHelpText>
<UserInputType>TextBox</UserInputType>
</ClaimType>
<ClaimType Id="identityProvider">
<DisplayName>Identity Provider</DisplayName>
<DataType>string</DataType>
<UserHelpText/>
</ClaimType>
</ClaimsSchema>
<ContentDefinitions>
<ContentDefinition Id="api.signuporsignin">
<LoadUri>~/tenant/default/unified.cshtml</LoadUri>
<RecoveryUri>~/common/default_page_error.html</RecoveryUri>
<DataUri>urn:com:microsoft:aad:b2c:elements:unifiedssp:1.0.0</DataUri>
<Metadata>
<Item Key="DisplayName">Signin and Signup</Item>
</Metadata>
</ContentDefinition>
</ContentDefinitions>
</BuildingBlocks>
<ClaimsProviders>
<!--
- Element: ClaimsProvider - SAMLAssert
- Details: The SAML Token Issuer Technical Profile
- This provides the Signed Assertion to the Service Provider
-->
<ClaimsProvider>
<DisplayName>SAML Token Issuer</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="SAMLAssert">
<DisplayName>SAML Assertion</DisplayName>
<Protocol Name="None" />
<OutputTokenFormat>SAML2</OutputTokenFormat>
<CryptographicKeys>
<Key Id="SamlMessageSigning" StorageReferenceId="B2C_1A_SAMLCERT" />
<Key Id="SamlAssertionSigning" StorageReferenceId="B2C_1A_SAMLCERT" />
<Key Id="SamlAssertionDecryption" StorageReferenceId="B2C_1A_SAMLCERT" />
</CryptographicKeys>
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
<!--
- Element: ClaimsProvider - TESTIDP
- Details: The SAML Identitity Provider Technical Profile
-
-->
<ClaimsProvider>
<DisplayName>SAML IDP</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="TESTIDP">
<DisplayName>SAML IDP</DisplayName>
<Protocol Name="SAML2" />
<Metadata>
<Item Key="IssuerUri">%THISWEBAPP%</Item>
<Item Key="PartnerEntity">%THISWEBAPP%/Metadata</Item>
<Item Key="WantsSignedAssertions">false</Item>
<Item Key="ResponsesSigned">false</Item>
<!-- Added for IDP Initated SSO From the Extrnal IDP SIde -->
<Item Key="IdpInitiatedProfileEnabled">true</Item>
</Metadata>
<CryptographicKeys>
<Key Id="SamlMessageSigning" StorageReferenceId="B2C_1A_SAMLCERT" />
<Key Id="SamlAssertionSigning" StorageReferenceId="B2C_1A_SAMLCERT" />
<Key Id="SamlAssertionDecryption" StorageReferenceId="B2C_1A_SAMLCERT" />
</CryptographicKeys>
<!-- Input Claims used fro IDP Initiated SSO -->
<InputClaims>
<InputClaim ClaimTypeReferenceId="Title" />
<InputClaim ClaimTypeReferenceId="givenName" PartnerClaimType="FirstName" />
<InputClaim ClaimTypeReferenceId="surname" PartnerClaimType="LastName" />
<InputClaim ClaimTypeReferenceId="signInName" PartnerClaimType="UID" />
<InputClaim ClaimTypeReferenceId="identityProvider" DefaultValue="TESTIDP" />
<InputClaim ClaimTypeReferenceId="objectId" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="Title" />
<OutputClaim ClaimTypeReferenceId="givenName" PartnerClaimType="FirstName" />
<OutputClaim ClaimTypeReferenceId="surname" PartnerClaimType="LastName" />
<OutputClaim ClaimTypeReferenceId="signInName" PartnerClaimType="UID" />
<OutputClaim ClaimTypeReferenceId="identityProvider" DefaultValue="TESTIDP" />
<OutputClaim ClaimTypeReferenceId="objectId" />
</OutputClaims>
<EnabledForUserJourneys>true</EnabledForUserJourneys>
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
<!--
- Element: ClaimsProvider - TPEngine
- Details: This is the Default TPEngine Technical Profile required for B2C
-
-->
<ClaimsProvider>
<DisplayName>Trustframework Policy Engine TechnicalProfiles</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="TpEngine_c3bd4fe2-1775-4013-b91d-35f16d377d13">
<DisplayName>Trustframework Policy Engine Default Technical Profile</DisplayName>
<Protocol Name="None" />
<Metadata>
<Item Key="url">{service:te}</Item>
</Metadata>
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
</ClaimsProviders>
<UserJourneys>
<!--
- Element: UserJourney - TESTIDPJourney
- Details: This is the TEST SAML User Journey
-->
<UserJourney Id="TESTIDPJourney">
<OrchestrationSteps>
<!-- Claims Exchange Must be the first Orchistration Step for IDP Iniated SSO -->
<OrchestrationStep Order="1" Type="ClaimsExchange">
<ClaimsExchanges UserIdentity="false">
<ClaimsExchange Id="TESTIDPExchange" TechnicalProfileReferenceId="TESTIDP" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="2" Type="SendClaims" CpimIssuerTechnicalProfileReferenceId="SAMLAssert" />
</OrchestrationSteps>
</UserJourney>
</UserJourneys>
<!--
- Element: RelyingParty
- Details: This section contains the Service Provider configuration
-->
<RelyingParty>
<DefaultUserJourney ReferenceId="TESTIDPJourney" />
<TechnicalProfile Id="SAMLRP">
<DisplayName>PolicyProfile</DisplayName>
<Protocol Name="SAML2" />
<OutputTokenFormat>SAML2</OutputTokenFormat>
<Metadata>
<Item Key="PartnerEntity">%THISWEBAPP%/Metadata</Item>
<!-- Added for IDP Initated SSO From the B2C Side -->
<Item Key="IdpInitiatedProfileEnabled">true</Item>
</Metadata>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="objectId" PartnerClaimType="SubjectNamingInfo" DefaultValue="NOSUBSET" />
<OutputClaim ClaimTypeReferenceId="title" />
<OutputClaim ClaimTypeReferenceId="givenName" />
<OutputClaim ClaimTypeReferenceId="surname" />
<OutputClaim ClaimTypeReferenceId="signInName" />
<OutputClaim ClaimTypeReferenceId="identityProvider" />
</OutputClaims>
<SubjectNamingInfo ClaimType="SubjectNamingInfo" Format="urn:oasis:names:tc:SAML:2.0:nameid-format:transient"/>
</TechnicalProfile>
</RelyingParty>
</TrustFrameworkPolicy>
32 changes: 32 additions & 0 deletions source-code/Pages/B2CPolicy.cshtml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
@page
@model SAMLTEST.Pages.B2CPolicyModel
@{
ViewData["Title"] = "B2CPolicy";
Layout = "~/Pages/_Layout.cshtml";
}

<h2>B2C Policy Download</h2>

@section Scripts {
@{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}


@using (Html.BeginForm())
{
<div class="form-group">

@Html.LabelFor(m => m.Tenant)
@Html.TextBoxFor(m => m.Tenant, htmlAttributes: new { @class = "form-control", @placeholder = "Enter Tenant Id" })
<small id="emailHelp" class="form-text text-muted">The Tenant name with or without onmicrosoft.com.</small>
<div class="text-danger">@Html.ValidationMessageFor(m => m.Tenant)</div>
</div>
<div class="form-group">
@Html.LabelFor(m => m.Policy)
@Html.TextBoxFor(m => m.Policy, htmlAttributes: new { @class = "form-control", @placeholder = "Enter B2C Policy" })
<small id="emailHelp" class="form-text text-muted">The Policy name with or without B2C_1A_</small>
<div class="text-danger">@Html.ValidationMessageFor(m => m.Policy)</div>
</div>
<input id="Button1" type="submit" value="Download Policy" class="btn btn-primary" />
}

47 changes: 47 additions & 0 deletions source-code/Pages/B2CPolicy.cshtml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.IO;
using System.Reflection;
using SAMLTEST.SAMLObjects;

namespace SAMLTEST.Pages
{
public class B2CPolicyModel : PageModel
{
[DisplayName("Tenant Name"), Required]
public string Tenant { get; set; }
[DisplayName("B2C Policy"), Required]
public string Policy { get; set; } = "SAMLTEST";

public IActionResult OnGet(String Tenant, String Policy)
{
if (String.IsNullOrEmpty(Tenant) || String.IsNullOrEmpty(Policy))
return Page();
else
return OnPost(Tenant, Policy);
}

public IActionResult OnPost(String Tenant, String Policy)
{
Policy = Policy.StartsWith("B2C_1A_") ? Policy : "B2C_1A_" + Policy;
Tenant = Tenant.ToLower().Contains("onmicrosoft.com") ? Tenant : Tenant + ".onmicrosoft.com";

Assembly _assembly = Assembly.GetExecutingAssembly();
Stream polstream = _assembly.GetManifestResourceStream("SAMLTEST.B2CPolicyTemplate.xml");
StreamReader _textStreamReader = new StreamReader(polstream);
String Polfile = _textStreamReader.ReadToEnd();
Polfile = Polfile.Replace("%TENANTID%",Tenant);
Polfile = Polfile.Replace("%POLICYID%", Policy);
Polfile = Polfile.Replace("%THISWEBAPP%", SAMLHelper.GetThisURL(this));
//Comment out the below line if you would prefer to show on the page
Response.Headers.Add("Content-Disposition", "attachment; filename" + Policy + ".xml");
return Content(Polfile, "text/xml");
}
}
}
16 changes: 16 additions & 0 deletions source-code/Pages/Error.cshtml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
@page
@model ErrorModel
@{
ViewData["Title"] = @Model.Title;
}

<h1 class="text-danger">@Model.Title</h1>
<h2 class="text-danger">@Model.ErrorMessage</h2>

@if (Model.ShowRequestId)
{
<p>
<strong>Request ID:</strong> <code>@Model.RequestId</code>
</p>
}

59 changes: 59 additions & 0 deletions source-code/Pages/Error.cshtml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
using Microsoft.AspNetCore.Diagnostics;
using Microsoft.AspNetCore.Mvc.RazorPages;
using System;
using System.Diagnostics;

namespace SAMLTEST.Pages
{
/// <summary>
/// This is the Error Page Model
/// </summary>
public class ErrorModel : PageModel
{
public string RequestId { get; set; }
public string ErrorMessage { get; set; }
public string Title { get; set; }

public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);

/// <summary>
/// This method is used to display custom errors
/// StatusCode - will be passed from UseStatusCodePagesWithRedirects in the
/// StartUp class. Error
/// ErrorMessage - Will be populated from SAML Responses
/// All other unhandled messages will also be displayed.
/// </summary>
public void OnGet(string ErrorMessage,string StatusCode="200")
{
RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier;
var exception = HttpContext.Features.Get<IExceptionHandlerFeature>();
this.Title = "Unhandled Error.";
switch (StatusCode)
{
case "404":
this.Title = "Page does not exist.";
break;
case "200":
if(!String.IsNullOrEmpty(ErrorMessage))
{
this.Title = "SAML Error";
this.ErrorMessage = ErrorMessage;
}
else
{
if (null != exception)
{
this.ErrorMessage = exception.Error.Message;
}
}
break;
default:
if (null != exception)
{
this.ErrorMessage = exception.Error.Message;
}
break;
}
}
}
}
29 changes: 29 additions & 0 deletions source-code/Pages/IDP/AuthNRequest.cshtml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
@page
@model SAMLTEST.Pages.IDP.AuthNRequestModel
@{
ViewData["Title"] = "AuthNRequest";
Layout = "~/Pages/_Layout.cshtml";
}

<h2>Demo Login Page</h2>


<form>
<div class="form-group">
<label for="exampleInputEmail1">Username</label>
<input type="text" class="form-control" id="usernameid" aria-describedby="usernameHelp" placeholder="Enter Username">
<small id="emailHelp" class="form-text text-muted">You really don't need to fill this out as its a demo app :)</small>
</div>
<div class="form-group">
<label for="exampleInputPassword1">Password</label>
<input type="password" class="form-control" id="exampleInputPassword1" placeholder="Password">
</div>
</form>


<form action="@Model.ACS" method="post" >
<input id="SAMLResponse" name="SAMLResponse" type="hidden" value="@Html.Raw(@Model.SAMLResponse)" />
<input id="RelayState" name="RelayState" type="hidden" value="@Html.Raw(@Model.RelayState)" />
<input id="Button1" type="submit" value="Login" class="btn btn-primary"/>
</form>

Loading

0 comments on commit ace9702

Please sign in to comment.