zondag 25 maart 2012
Domain Driven Development - Business Logic in Domain Objects
Introduction
One of the recurring questions in developing software in C# is where to put Business Logic. It's quite easy to scatter Business Rules throughout the system, in such a way that it will be very hard to maintain the code.
Let's take for example this Business Rule:
When a Phase is closed, no more Tasks can be added to it.
If we build a simple system these two objects can look like this in code:
public class Phase
{
public int Id { get; set; }
public bool IsClosed { get ; set ; }
public List<Task> Tasks { get ; set ; }
}
public class Task
{
public string Name { get ; set ; }
}
And we can interact with Phase and Task like this:
var phase = _phaseRepository.GetById(1);
if(!phase.IsClosed)
{
phase.Tasks.Add(new Task());
}
What is the problem with this code?
The problem with the code above is that if in one place we forget to check if the Phase is closed, we can add Tasks to a closed Phase. So, mistakes are quite easy to make in this case.
Another problem is that we have to copy the check for IsClosed every time we want to add Tasks to a Phase in another part of the system.
And a third problem is that whenever we add another condition to the Business Rule, we have to go through all code where we Add Tasks and add code for the new condition.
What is the solution?
It's better to move this Business Rule to the Phase object, like this:
public class Phase
{
public int Id { get; set; }
public bool IsClosed { get; set; }
public List <Task> Tasks { get; set; }
public void AddTask(Task task)
{
if(IsClosed)
{
throw new Exception("Can't add tasks to closed Phase.");
}
Tasks.Add( task );
}
}
Now we can simply use the code above like this:
var phase = PhaseRepository.GetById(1);
phase.AddTask(new Task());
This looks a lot better. And if we add another property to the Phase which has to be checked when we add a Task, we only have to change the Phase object, and not the code in the controller.
We're not there yet
Although we now have a nice AddTask method, we can still easily get around it, because the List<Task> Tasks on Phase is public and has an Add method. So we can do this:
var phase = PhaseRepository.GetById(1);
phase.AddTask(new Task());
phase.IsClosed = true;
phase.Tasks.Add(new Task());
We can solve this by making the Tasks list readonly:
public class Phase
{
public int Id { get; set; }
public bool IsClosed { get; set; }
private List<Task> _tasks;
public ReadOnlyCollection<Task> ReadOnlytasks
{
get
{
return new ReadOnlyCollection<Subforum>(_tasks);
}
}
}
And you will see that the Add methods isn't supported anymore. So everytime we want to add a Task to a Phase we have to use the AddTask method.
var phase = PhaseRepository.GetById(1);
phase.AddTask(new Task());
phase.Tasks.Add(new Task());
Entity Framework
So what if we use Entity Framework, does this solution work? No it doesn't work completely, because Entity Framework doesn't support ReadOnlyCollection<T>. But I have a solution for that, as you can read in the blog post I wrote:
Readonly collections with Entity Framework
zaterdag 17 maart 2012
Created a website to try Azure out
I've created www.BbsFactory.com which basically is a website with which you can create a hosted forum. So in other words it's a Forum as a Service.
This site helps me to try out things on Windows Azure, which works a lot better then trying yet another Hello World sample.
This site helps me to try out things on Windows Azure, which works a lot better then trying yet another Hello World sample.
donderdag 9 februari 2012
Azure webrole federated by ACS
Problem
If you deploy a Web Role secured with ACS, and you have two or more instances of your Web Role, you probably have seen this error message:
Key not valid for use in specified state
The problem is that the cookie that is created if you log in with ACS, is encrypted with the machine key by default. If the cookie is created by one of the instances, the cookie can not be read by the other instances because their machine key is different.
Solution
There is a solution to this: encrypt the cookie using a certificate that is known to all instances.
How to
I'll show you how a self-signed certificate can be used to encrypt the ACS cookie.
Create a certificate
First, on your dev machine open IIS and create a self-signed certificate. This certificate can be used on your Azure development environment as well as on the staging and production environment.
Give rights on certificate
To be able to use it on your development machine do the following:
Go to MMC to view the certificates in the Personal store of Local Computer. Right click on the created certificate and choose [All Tasks] [Manage Private Keys...]. Add the username 'Network Service' with read rights.
Network Service is the account under which the Azure simulation environment is executing. It now has rights to read the self-signed certificate.
Add certificate to config
Next we have to use this certificate in our Web Role. Open the ServiceDefinition.csdef file. Add the following code under the WebRole node:
<Certificates>
<Certificate name="CookieEncryptionCertificate"
storeLocation="LocalMachine"
storeName="My" />
</Certificates>
Now your definition file should look something like this:
<?xml version="1.0" encoding="utf-8"?>
<ServiceDefinition name="AutofacTestAzure"
xmlns="http://schemas.micros[...]">
<WebRole name="[name]" vmsize="Small">
<Certificates>
<Certificate name="CookieEncryptionCertificate"
storeLocation="LocalMachine"
storeName="My" />
</Certificates>
...
</ WebRole >
< ServiceDefinition >
Add the following text to all cscfg-files where you want to use the certificate:
<Certificates>
<Certificate name="Cookie" thumbprint="[thumbprint]" thumbprintAlgorith="sha1" />
</Certificates >
FederatedAuthentication.ServiceConfigurationCreated += FedAuthServiceConfigCreated;
protected void Application_Start()
{
FederatedAuthentication.ServiceConfigurationCreated +=
FedAuthServiceConfigCreated;
...
}
private void FedAuthServiceConfigCreated(
object sender, ServiceConfigurationCreatedEventArgs e)
{
// Use the <serviceCertificate> to protect the cookies
// that are sent to the client.
var readonlyCookietransformList = new CookieTransform[]
{
new DeflateCookieTransform(),
new RsaEncryptionCookieTransform(e.ServiceConfiguration.ServiceCertificate)
}.ToList().AsReadOnly();
var sessionSecurityTokenHandler =
new SessionSecurityTokenHandler(readonlyCookietransformList);
e.ServiceConfiguration.SecurityTokenHandlers
.AddOrReplace(sessionSecurityTokenHandler);
}
That's all. You can upload your Web Role to Azure and everything works.
If you deploy a Web Role secured with ACS, and you have two or more instances of your Web Role, you probably have seen this error message:
Key not valid for use in specified state
The problem is that the cookie that is created if you log in with ACS, is encrypted with the machine key by default. If the cookie is created by one of the instances, the cookie can not be read by the other instances because their machine key is different.
Solution
There is a solution to this: encrypt the cookie using a certificate that is known to all instances.
How to
I'll show you how a self-signed certificate can be used to encrypt the ACS cookie.
Create a certificate
First, on your dev machine open IIS and create a self-signed certificate. This certificate can be used on your Azure development environment as well as on the staging and production environment.
Give rights on certificate
To be able to use it on your development machine do the following:
Go to MMC to view the certificates in the Personal store of Local Computer. Right click on the created certificate and choose [All Tasks] [Manage Private Keys...]. Add the username 'Network Service' with read rights.
Network Service is the account under which the Azure simulation environment is executing. It now has rights to read the self-signed certificate.
Add certificate to config
Next we have to use this certificate in our Web Role. Open the ServiceDefinition.csdef file. Add the following code under the WebRole node:
<Certificates>
<Certificate name="CookieEncryptionCertificate"
storeLocation="LocalMachine"
storeName="My" />
</Certificates>
Now your definition file should look something like this:
<?xml version="1.0" encoding="utf-8"?>
<ServiceDefinition name="AutofacTestAzure"
xmlns="http://schemas.micros[...]">
<WebRole name="[name]" vmsize="Small">
<Certificates>
<Certificate name="CookieEncryptionCertificate"
storeLocation="LocalMachine"
storeName="My" />
</Certificates>
...
</ WebRole >
< ServiceDefinition >
Add the following text to all cscfg-files where you want to use the certificate:
<Certificates>
<Certificate name="Cookie" thumbprint="[thumbprint]" thumbprintAlgorith="sha1" />
</Certificates >
You can find the [thumbprint] value if you view the Details of the certificate in IIS. Make sure you remove the invisble space at the start of the thumbprint value, after you paste it into the xml.
Example thumbprint value "5a 0a e8 80 f4 ..."
At this position "(here)5a 0a e8 80 f4 ..." you'll notice an invisible space. Make sure you remove that.
Use certificate in code
We can now make some changes in code, so the certificate will be used to encrypt the cookie.
Add this code to the Application_Start event in the global.asax:
So it will look like this:
protected void Application_Start()
{
FederatedAuthentication.ServiceConfigurationCreated +=
FedAuthServiceConfigCreated;
...
}
Add this method to the global.asax.cs:
private void FedAuthServiceConfigCreated(
object sender, ServiceConfigurationCreatedEventArgs e)
{
// Use the <serviceCertificate> to protect the cookies
// that are sent to the client.
var readonlyCookietransformList = new CookieTransform[]
{
new DeflateCookieTransform(),
new RsaEncryptionCookieTransform(e.ServiceConfiguration.ServiceCertificate)
}.ToList().AsReadOnly();
var sessionSecurityTokenHandler =
new SessionSecurityTokenHandler(readonlyCookietransformList);
e.ServiceConfiguration.SecurityTokenHandlers
.AddOrReplace(sessionSecurityTokenHandler);
}
If you press F5 now, you'll see everything works locally.
Time to get things working in the cloud
There's only one thing we have to do to get this working in the cloud, and that's to upload the certificate to our Hosted Service.
First, go to the created certificate in IIS and export it to a .pfx file. Next go to the management portal and import the certificate to the Certificates of the Hosted Service.
First, go to the created certificate in IIS and export it to a .pfx file. Next go to the management portal and import the certificate to the Certificates of the Hosted Service.
That's all. You can upload your Web Role to Azure and everything works.
donderdag 12 januari 2012
Secure WCF Service with ADFS
Introduction
This article describes how to secure a WCF Service by ADFS.
ADFS
Create a Relying Party in ADFS. Use these settings:
Display name = Name of the service
Profile = AD FS 2.0 Profile
Certificate = encryption certificate
Configure URL = uncheck both options
Identifiers = complete url of the service, for example https://www.hostname.com/service1.svc
Auth = Permit all users
Certificates
These are the needed certificates:
SSL certificate for the WCF service in IIS
- Create this with CertSrv. Make sure the CN Name is the same as the hostname used for the WCF Service in IIS.
- Import without private key into Trusted People store
Encryption certificate for the relying party in ADFS
- Import with private key into Personal store
- Give rights to IIS_IUSRS
Credentials certificate for the client
- Import with private key into Personal Store
- Give rights to the windows account the application is running under, or app pool the website is running under
Console Application code
string userCertificateThumbprint = "[thumbprint of Credentials certificate for the client]";
var channelFactory =
new ChannelFactory<ISync>("ServiceReference1.ISync");
// Set certificate for user
channelFactory.Credentials.ClientCertificate.SetCertificate(
StoreLocation.LocalMachine,
StoreName.My,
X509FindType.FindByThumbprint,
userCertificateThumbprint);
var wcfClient = channelFactory.CreateChannel();
var res = wcfClient.Foo();
Active Directory
Create a user in Active Directory.
Login to CertSrv and with the created user, and create a certificate. It will be automatically set in AD.
The created certificate will be stored in the Personal store of the Current User. It should be imported into the Personal store of Local Computer this way:
- Open MMC
- Go to the Personal Store of Current User
- Export the created certificate including it's private key
- Go to the Personal Store of Local Computer
- Import the certificate
- Make a note of the thumbprint, it should be put into the Console Application config later.
WCF Service configuration
Add this code to the app.config of the WCF Service:
<configSections>
<section
name="microsoft.identityModel"
type="Microsoft.IdentityModel.Configuration.MicrosoftIdentityModelSection, Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
</configSections>
<system.serviceModel>
<services>
<service
name="[Name of the service]"
behaviorConfiguration="federationBehavior">
<endpoint
address=""
binding="customBinding"
contract="[Interface implemented by the WCF Service]"
<!-- example: Wcf.IService1 -->
bindingConfiguration="WifActiveFedBinding" />
<endpoint
address="mex"
binding="mexHttpsBinding"
contract="IMetadataExchange" />
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="federationBehavior">
<federatedServiceHostConfiguration />
<serviceMetadata httpsGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
<serviceCredentials>
<!-- Encryption certificate for the relying party in ADFS -->
<serviceCertificate
findValue="[thumbprint]"
storeLocation="LocalMachine"
storeName="My"
x509FindType="FindByThumbprint" />
</serviceCredentials>
</behavior>
</serviceBehaviors>
</behaviors>
<extensions>
<behaviorExtensions>
<add
name="federatedServiceHostConfiguration"
type="Microsoft.IdentityModel.Configuration.ConfigureServiceHostBehaviorExtensionElement, Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
</behaviorExtensions>
</extensions>
<bindings>
<customBinding>
<binding name="WifActiveFedBinding">
<security
authenticationMode="SecureConversation"
messageSecurityVersion="WSSecurity11WSTrust13WSSecureConversation13WSSecurityPolicy12BasicSecurityProfile10"
requireSecurityContextCancellation="false">
<secureConversationBootstrap
authenticationMode="IssuedTokenOverTransport"
messageSecurityVersion="WSSecurity11WSTrust13WSSecureConversation13WSSecurityPolicy12BasicSecurityProfile10">
<issuedTokenParameters>
<additionalRequestParameters>
<AppliesTo>
<EndpointReference>
<!-- Base address of the WCF Service. -->
<!-- Use a domain name, don't use an ip address. -->
<!-- Domain name must be same as defined in certificate -->
<Address>https://[DomainName]/</Address>
</EndpointReference>
</AppliesTo>
</additionalRequestParameters>
</issuedTokenParameters>
</secureConversationBootstrap>
</security>
<httpsTransport />
</binding>
</customBinding>
</bindings>
</system.serviceModel>
<microsoft.identityModel>
<service>
<audienceUris>
<!-- All endpoints that need to be secured. -->
<add value="https://[DomainName]/[ServicePath]" />
<!-- example: https://www.mywcfservice.com/wcf.service1.svc -->
</audienceUris>
<issuerNameRegistry
type="Microsoft.IdentityModel.Tokens.ConfigurationBasedIssuerNameRegistry, Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35">
<trustedIssuers>
<!--Signing certificaat of ADFS -->
<add
thumbprint="thumbprint of signing certificaat of ADFS"
name="http://[adfs server]/adfs/services/trust" />
</trustedIssuers>
</issuerNameRegistry>
</service>
</microsoft.identityModel>
Console Application - app.config
<configSections>
<section name="microsoft.identityModel"
type="Microsoft.IdentityModel.Configuration.MicrosoftIdentityModelSection, Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
</configSections>
<system.serviceModel>
<bindings>
<customBinding>
<binding name="WifActiveFedClientBinding">
<security
authenticationMode="SecureConversation"
messageSecurityVersion="WSSecurity11WSTrust13WSSecureConversation13WSSecurityPolicy12BasicSecurityProfile10">
<secureConversationBootstrap
authenticationMode="IssuedTokenOverTransport"
messageSecurityVersion="WSSecurity11WSTrust13WSSecureConversation13WSSecurityPolicy12BasicSecurityProfile10">
<issuedTokenParameters keyType="SymmetricKey" tokenType="">
<additionalRequestParameters>
<trust:SecondaryParameters
xmlns:trust="http://docs.oasis-open.org/ws-sx/ws-trust/200512">
<trust:KeyType>http://docs.oasis-open.org/ws-sx/ws-trust/200512/SymmetricKey</trust:KeyType>
<AppliesTo xmlns="http://schemas.xmlsoap.org/ws/2004/09/policy">
<EndpointReference xmlns="http://www.w3.org/2005/08/addressing">
<Address>http://mandatorybutdoesntmatter/</Address>
</EndpointReference>
</AppliesTo>
</trust:SecondaryParameters>
</additionalRequestParameters>
<issuer
address = "https://[adfs hostname]/adfs/services/trust/13/certificatemixed"
binding="ws2007HttpBinding"
bindingConfiguration="certificateTrust" />
</issuedTokenParameters>
</secureConversationBootstrap>
</security>
<httpsTransport/>
</binding>
</customBinding>
<ws2007HttpBinding>
<binding name="certificateTrust" >
<security mode="TransportWithMessageCredential">
<message
clientCredentialType="Certificate" establishSecurityContext="false" />
</security>
</binding>
</ws2007HttpBinding>
</bindings>
<client>
<endpoint
address="https://[DomainName]/[ServicePath]"
<!-- example: https://www.mywcfservice/wcf.service1.svc -->
binding="customBinding"
bindingConfiguration="WifActiveFedClientBinding"
contract="ServiceReference1.ISync"
name="ServiceReference1.ISync">
<identity>
<certificateReference
storeName="My"
storeLocation="LocalMachine"
x509FindType="FindByThumbprint"
findValue="[thumbprint of SSL certificate for the WCF service in IIS]" />
</identity>
</endpoint>
</client>
</system.serviceModel>
</configuration>
vrijdag 18 november 2011
Turn off Home Realm Discovery in ADFS 2.0
Introduction
When you've added another Claims Provider Trust to ADFS, next to ADFS, you'll see the HomeRealmDiscovery page when you try to logon. This page looks like this:
Problem
Most users don't know what to select here. So you wouldn't want them to see this page.
Solution
You can add a setting to your application web.config which tells adfs to use a specific Claims Provider. This setting is called homeRealm. The value can be found in Adfs Management tool.
<federatedAuthentication>
<wsFederation
passiveRedirectEnabled="true"
issuer="[adfs]"
realm="[website url]"
requireHttps="true"
homeRealm="[Claims provider identifier]" />
When you've added another Claims Provider Trust to ADFS, next to ADFS, you'll see the HomeRealmDiscovery page when you try to logon. This page looks like this:
Problem
Most users don't know what to select here. So you wouldn't want them to see this page.
Solution
You can add a setting to your application web.config which tells adfs to use a specific Claims Provider. This setting is called homeRealm. The value can be found in Adfs Management tool.
<federatedAuthentication>
<wsFederation
passiveRedirectEnabled="true"
issuer="[adfs]"
realm="[website url]"
requireHttps="true"
homeRealm="[Claims provider identifier]" />
...
</federatedAuthentication>
dinsdag 18 oktober 2011
Add a custom STS to ADFS
Introduction
Out of the box ADFS can authenticate with username/password against Active Directory. You can easily extend ADFS to use a custom STS. This makes it possible for example to authenticate against SQL Server. This blog post will show you how to do that.
Prerequisites
You need at least two machines:
Machine 1, which runs Windows Server 2008 R2 and needs the following:
Active directory installed
ADFS v2.0 installed
Machine 2, which will be used as a development machine and needs the following:
Windows Identiy Foundation
IIS 7
Create a Custom STS
Start Visual Studio 2010 as administrator. (If you don't run as administrator, you can get a access denied error when you create the website).
Create a new website:
Select ASP.Net Security Token Service:
Give it a name, for example: MyCustomSts
Make sure .NET Framework 4 is selected
Click OK
When the Solution is created, open the file CustomSecurityTokenService.cs.
Go to the method GetScope. At the end you will see this line:
scope.ReplyToAddress = scope.AppliesToAddress;
This line can cause problems, because our CustomSTS will not always redirect back to the correct url of ADFS. So we change it by hand to:
scope.ReplyToAddress = "https://[domain name of ADFS]/adfs/ls/";
When i insert my ADFS domain name the line will look like this:
scope.ReplyToAddress = "https://w2k8x64ad.dev.local/adfs/ls/";
Of course it's best to put this into the appsettings of the web.config. You can then use this:
scope.ReplyToAddress = WebConfigurationManager.AppSettings["AdfsAddress"];
Name of our Custom STS
We have to set the name (also know as Identifier) of our Custom STS in the web.config.
Find the following appsetting:
<add key="IssuerName" value="PassiveSigninSTS"/>
Change the value to the ID of our Custom STS, for example: MyCustomSts
<add key="IssuerName" value="MyCustomSts"/>
Token signing
Our CustomSTS will return tokens which have to be signed by a certificate. So we need to have a certificate and we have to let our Custom STS know that is has to use that certificate. out of the box the ASP.Net Security Token Service template will do that for us. I think it's a good idea to create a custom signing-token for practice.
First we have to create a certificate with which our Custom STS will sign tokens.
1. Start IIS
2. Open the window for Server Certificates
At the right hand side of the window you can select the action Create Self-Signed Certificate...
Give the certificate a name: MyCustomStsCert.
Click OK.
You can see the certificate is created with the name we entered.
Next we install the certificate into the certificate store of the local machine. To do this export the certificate:
Give the exported certificate a name and a password:
To import the exported certificate into the local certificate store do this:
1. Open MMC (start -> run -> mmc)
2. In MMC select [file][add/remove add-in]
3. Select Certificates
4. Click Add
5. Select Computer Account
6. Click Next
7. Click Finish
8. Expand Certificates
9. Expand Personal
10. Right click on Certificates below Personal and select All Tasks and Import

Next select the certificate that we exported earlier.
After the certificate is imported, double click on it to see the details.

Find the subject on the Details tab. Copy the value and paste it into the web.config of our Custom STS:
<add key="SigningCertificateName" value="CN=Win7642"/>
Note: remove the spaces in the value, or else our Custom STS can'tfind the certificate.
So now we've made a certificate to sign the tokens and have let our Custom STS know that is has to use that certificate to sign the tokens. We only have to give our Custom STS the right to read the token. But before we can do that we have to deploy our Custom STS to IIS.
Deploy to IIS
We are finished with creating the Custom STS. Now we have to publish it from Visual Studio to for example [drive]\inetpub\wwwroot\MyCustomSTS. But you can choose any folder you want.
Because our Custom STS needs to run under https, we have to create a certificate. For now we can use a self-signed certificate. But when we you take this into production, you have to get a real certificate.
Next we create a new site in IIS with the published project from the previous part.
Use binding https.

Note: You have to use a certificate to run a website under https. You can use the certificate MyCustomStsCert we created earlier, but that would be not really the way to go, since in production you will never use the signing certificate to secure the website. So in our example i've used another self-signed certificate called STSTestCert.
Note: After you click OK and the website is created, don't forget to set the .Net Framework version to 4.0 on the app pool.
Connect Custom STS to ADFS
After we created a Custom STS in the previous steps, we now have to tell ADFS there is a new Claims Provider. We also have to give ADFS our signing certificate.
Export Signing certificate for ADFS
Go to MyCustomStsCert in MMC and select Export
Click Next on every screen and don't change the default settings.
Finally the certificate is exported. Copy the exported certificate to the ADFS server.
Import Signing certificate for ADFS
Everything we did previously was executed on the machine running our Custom STS. Now we move over to the ADFS server.
Go to AD FS 2.0 Management.
Right click on Claims Provider Trusts
Select Add Claims Provider Trust...

Click start on the Welcome screen.
Select Enter claims provider trust data manually.
Click Next.

Enter the name of the claims provider: MyCustomSts.

Click Next.

Select Enable support for the WS-Federation Passive protocol.
Enter the url to the Custom STS.
Click Next.

Note: make sure your Custom STS server can ping your ADFS server. And try browsing to the Custom STS url.
Enter an ID for the Custom STS.
Note: This ID must be the same as the IssuerName entered in the web.config of the Custom STS:
<add key="IssuerName" value="MyCustomSts"/>
Click Next.

Add the signing certificate we exported and copied to the ADFS server.
Click Next.
Click Next.

Click Close.

Add a Claim Rule. Select Pass Through or Filter an Incoming Claim.
Click Next.

Our Custom STS delivers two claims out of the box: name and role. We can now add rules to set which claims are delivered from our Custom STS to ADFS.
Let's start with a rule for the Name claim. Enter a name for the claim rule, for example name.
select the incoming claim type. For example Name.
click Finish.

We get a warning we can ignore.
Click Yes.

We can now add another claim rule for the Role claim. Or Click Apply and OK.

Finished
We are finished now. if you browse to your relying party, you can authenticate with the new Custom STS.
Out of the box ADFS can authenticate with username/password against Active Directory. You can easily extend ADFS to use a custom STS. This makes it possible for example to authenticate against SQL Server. This blog post will show you how to do that.
Prerequisites
You need at least two machines:
Machine 1, which runs Windows Server 2008 R2 and needs the following:
Active directory installed
ADFS v2.0 installed
Machine 2, which will be used as a development machine and needs the following:
Windows Identiy Foundation
IIS 7
Create a Custom STS
Start Visual Studio 2010 as administrator. (If you don't run as administrator, you can get a access denied error when you create the website).
Create a new website:
Select ASP.Net Security Token Service:
Give it a name, for example: MyCustomSts
Make sure .NET Framework 4 is selected
Click OK
When the Solution is created, open the file CustomSecurityTokenService.cs.
Go to the method GetScope. At the end you will see this line:
scope.ReplyToAddress = scope.AppliesToAddress;
This line can cause problems, because our CustomSTS will not always redirect back to the correct url of ADFS. So we change it by hand to:
scope.ReplyToAddress = "https://[domain name of ADFS]/adfs/ls/";
When i insert my ADFS domain name the line will look like this:
scope.ReplyToAddress = "https://w2k8x64ad.dev.local/adfs/ls/";
Of course it's best to put this into the appsettings of the web.config. You can then use this:
scope.ReplyToAddress = WebConfigurationManager.AppSettings["AdfsAddress"];
Name of our Custom STS
We have to set the name (also know as Identifier) of our Custom STS in the web.config.
Find the following appsetting:
<add key="IssuerName" value="PassiveSigninSTS"/>
Change the value to the ID of our Custom STS, for example: MyCustomSts
<add key="IssuerName" value="MyCustomSts"/>
Token signing
Our CustomSTS will return tokens which have to be signed by a certificate. So we need to have a certificate and we have to let our Custom STS know that is has to use that certificate. out of the box the ASP.Net Security Token Service template will do that for us. I think it's a good idea to create a custom signing-token for practice.
First we have to create a certificate with which our Custom STS will sign tokens.
1. Start IIS
2. Open the window for Server Certificates
Give the certificate a name: MyCustomStsCert.
Click OK.
You can see the certificate is created with the name we entered.
Next we install the certificate into the certificate store of the local machine. To do this export the certificate:
1. Open MMC (start -> run -> mmc)
2. In MMC select [file][add/remove add-in]
3. Select Certificates
4. Click Add
5. Select Computer Account
6. Click Next
7. Click Finish
8. Expand Certificates
9. Expand Personal
10. Right click on Certificates below Personal and select All Tasks and Import

Next select the certificate that we exported earlier.
After the certificate is imported, double click on it to see the details.

Find the subject on the Details tab. Copy the value and paste it into the web.config of our Custom STS:
<add key="SigningCertificateName" value="CN=Win7642"/>
Note: remove the spaces in the value, or else our Custom STS can'tfind the certificate.
So now we've made a certificate to sign the tokens and have let our Custom STS know that is has to use that certificate to sign the tokens. We only have to give our Custom STS the right to read the token. But before we can do that we have to deploy our Custom STS to IIS.
Deploy to IIS
We are finished with creating the Custom STS. Now we have to publish it from Visual Studio to for example [drive]\inetpub\wwwroot\MyCustomSTS. But you can choose any folder you want.
Because our Custom STS needs to run under https, we have to create a certificate. For now we can use a self-signed certificate. But when we you take this into production, you have to get a real certificate.
Next we create a new site in IIS with the published project from the previous part.
Use binding https.

Note: You have to use a certificate to run a website under https. You can use the certificate MyCustomStsCert we created earlier, but that would be not really the way to go, since in production you will never use the signing certificate to secure the website. So in our example i've used another self-signed certificate called STSTestCert.
Note: After you click OK and the website is created, don't forget to set the .Net Framework version to 4.0 on the app pool.
Connect Custom STS to ADFS
After we created a Custom STS in the previous steps, we now have to tell ADFS there is a new Claims Provider. We also have to give ADFS our signing certificate.
Export Signing certificate for ADFS
Go to MyCustomStsCert in MMC and select Export
Click Next on every screen and don't change the default settings.
Finally the certificate is exported. Copy the exported certificate to the ADFS server.
Import Signing certificate for ADFS
Everything we did previously was executed on the machine running our Custom STS. Now we move over to the ADFS server.
Go to AD FS 2.0 Management.
Right click on Claims Provider Trusts
Select Add Claims Provider Trust...

Click start on the Welcome screen.
Select Enter claims provider trust data manually.
Click Next.

Enter the name of the claims provider: MyCustomSts.

Click Next.

Select Enable support for the WS-Federation Passive protocol.
Enter the url to the Custom STS.
Click Next.

Note: make sure your Custom STS server can ping your ADFS server. And try browsing to the Custom STS url.
Enter an ID for the Custom STS.
Note: This ID must be the same as the IssuerName entered in the web.config of the Custom STS:
<add key="IssuerName" value="MyCustomSts"/>
Click Next.

Add the signing certificate we exported and copied to the ADFS server.
Click Next.

Click Close.

Add a Claim Rule. Select Pass Through or Filter an Incoming Claim.
Click Next.

Our Custom STS delivers two claims out of the box: name and role. We can now add rules to set which claims are delivered from our Custom STS to ADFS.
Let's start with a rule for the Name claim. Enter a name for the claim rule, for example name.
select the incoming claim type. For example Name.
click Finish.

We get a warning we can ignore.
Click Yes.

We can now add another claim rule for the Role claim. Or Click Apply and OK.

Finished
We are finished now. if you browse to your relying party, you can authenticate with the new Custom STS.
Abonneren op:
Posts (Atom)










