How do I set up AD FS 2.0 with a SAML 1.1 profile using Powershell? - powershell

I’m working on a Windows 2008 R2 Enterprise server. I am trying to script the creation of a relying party trust for our production servers. I’ve written a powershell script. When I create the relying party trust manually, I selelct SAML 1.1 profile and everything works. When I create a script and use the powershell command Add-ADFSRelyingPartyTrust, it does not work the same and I get the following error:
The Federation Service could not authorize token issuance for caller 'xxxx\xxxxx
'. The caller is not authorized to request a token for the relying party 'https://example.com/forms/'. Please see event 501 with the same instance id for caller identity.
When I search on that error, it appears to me (though I could be wrong) that it’s a SAML 2.0 profile issue. But I need SAML 1.1.
Here is the code where I create the relying party trust:
Write-Host "Configuring relying party trust."
Add-ADFSRelyingPartyTrust -Identifier "$endpoint" -Name "$name" -WSFedEndpoint "$endpoint/" -SignatureAlgorithm "http://www.w3.org/2000/09/xmldsig#rsa-sha1"
Write-Host "Configuring rules."
$ruleSet = New-ADFSClaimRuleSet -ClaimRule 'c:[Type == "http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsaccountname", Issuer == "AD AUTHORITY"] => issue(store = "Active Directory", types = ("http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsaccountname", "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress", "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname", "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname"), query = ";userPrincipalName,mail,givenName,sn;{0}", param = c.Value);'
Set-ADFSRelyingPartyTrust -TargetIdentifier "$endpoint" -IssuanceTransformRules $ruleSet.ClaimRulesString
Write-Host "Done."
When I do a GetADFSRelyingPartyTrust on the relying trust that works and compare it to the one the does not work, see a difference in the rule template:
Works:
IssuanceAuthorizationRules : #RuleTemplate = "AllowAllAuthzRule"
=> issue(Type = "http://schemas.microsoft.com/authorization/claims/permit", Val
ue = "true");
Doesn’t Work:
IssuanceAuthorizationRules :
Any ideas on how to get rid of the above error message?

By asking the question and providing the above information I found my own answer:
I needed to pass the -IssuanceAuthorizationRules parameter to the command.
-IssuanceAuthorizationRules '#RuleTemplate = "AllowAllAuthzRule" => issue(Type = "http://schemas.microsoft.com/authorization/claims/permit", Value = "true");'

Related

Automate some Exchange Online functions

Having that Exchange Online functions are not yet included in MS Graph, and Exchange does not have its own SDK also, the only option I found to automate some functions in Exchange Online is ExchangeOnlineManagement PowerShell.
I used it for a while but Microsoft recently ceased the Basic Authentication. I'm using the Basic Authentication because it does not require any prior package installation.
The modern authentication use Connect-ExchangeOnline and requires the ExchangeOnlineManagement to be installed prior to any call. It's easy to install it on a physical server or a VM, but I'm relying on Azure services, like azure Web app, azure Function app, where I cannot install a package. now the situation is:
Basic authentication stopped working, and it's not an option
anymore. This was the only option to execute exchange PowerShell
commands from azure web app or function app.
In modern authentication, you rely on the ExchangeOnlineManagement
that must be installed prior to any call. since I work on web app, or function
app, I cannot install it.
there is no .net Exchange SDK.
MS Graph lack Exchange functions.
So what is the valid solution to execute Exchange command? Maybe the solution is available but I'm not aware of it.
My case is simple: Every night, Upon the expiration of an Azure user account:
I change some user properties on Azure (Available through MS Graph)
I need to set an auto reply message, so sender can know the email
account is no longer monitored. (NOT AVAILABLE)
You can use BasicAuthToOAuthConversion=true in the connection string and if for example you currently have user creds being passed in switch to using the ROPC flow with those creds and just pass the username and accesstoken where you passing the users password eg
PSCredential pSCredential = new PSCredential("user#blah.onmicrosoft.com", new NetworkCredential("", "pass##").SecurePassword);
string MailboxName = pSCredential.UserName;
string scope = "https://outlook.office365.com/.default";
string ClientId = "a0c73c16-a7e3-4564-9a95-2bdf47383716";
HttpClient Client = new HttpClient();
var TenantId = ((dynamic)JsonConvert.DeserializeObject(Client.GetAsync("https://login.microsoftonline.com/" + MailboxName.Split('#')[1] + "/v2.0/.well-known/openid-configuration").Result.Content.ReadAsStringAsync().Result)).authorization_endpoint.ToString().Split('/')[3];
PublicClientApplicationBuilder pcaConfig = PublicClientApplicationBuilder.Create(ClientId);
pcaConfig.WithTenantId(TenantId);
var TokenResult = pcaConfig.Build().AcquireTokenByUsernamePassword(new[] { scope }, pSCredential.UserName, pSCredential.Password).ExecuteAsync().Result;
System.Security.SecureString secureString = new System.Security.SecureString();
foreach (char c in ("bearer " + TokenResult.AccessToken))
secureString.AppendChar(c);
String WSManURIConnectionString = "https://outlook.office365.com/powershell-liveid?DelegatedOrg=" + MailboxName.Split('#')[1] + "&BasicAuthToOAuthConversion=true";
PSCredential credential = new PSCredential(MailboxName, secureString);
WSManConnectionInfo connectionInfo = new WSManConnectionInfo(new Uri(WSManURIConnectionString), "http://schemas.microsoft.com/powershell/Microsoft.Exchange", credential);
connectionInfo.AuthenticationMechanism = AuthenticationMechanism.Basic;
connectionInfo.SkipCACheck = true;
connectionInfo.SkipCNCheck = true;
connectionInfo.MaximumConnectionRedirectionCount = 4;
Runspace runspace = System.Management.Automation.Runspaces.RunspaceFactory.CreateRunspace(connectionInfo);
runspace.Open();
// Make a Get-Mailbox requst using the Server Argument
Command gmGetMailbox = new Command("get-mailbox");
gmGetMailbox.Parameters.Add("ResultSize", "Unlimited");
Pipeline plPileLine = runspace.CreatePipeline();
plPileLine.Commands.Add(gmGetMailbox);
Collection<PSObject> RsResultsresults = plPileLine.Invoke();
Dictionary<string, PSObject> gmResults = new Dictionary<string, PSObject>();
foreach (PSObject obj in RsResultsresults)
{
Console.WriteLine(obj.Members["WindowsEmailAddress"].Value.ToString());
}
Command gmGetUser = new Command("get-user");
plPileLine.Stop();
plPileLine.Dispose();

How to export certs with SAN extensions?

I have this PowerShell command that exports for me all issued certificates into a .csv file:
$Local = "$PSScriptRoot"
$File = "$Local\IssuedCerts.csv"
$Header = "Request ID,Requester Name,Certificate Template,Serial Number,Certificate Effective Date,Certificate Expiration Date,Issued Country/Region,Issued Organization,Issued Organization Unit,Issued Common Name,Issued City,Issued State,Issued Email Address"
certutil -view -out $Header csv > $File
This works fine, by the way I would like to format the output in a more readable manner, if its somehow possible, please let me know, too.
The point is I need to export all certificates which will expire soon, but I also need data from SAN Extensions from each certificate too be exported with.
Perhaps getting the certificates directly from the CertificateAuthority X509Store and reading the certificate extensions (one of which is the Subject Alt Names) using the ASNEncodedData class would do the trick?
Example code below on reading certificates from the given store and printing out their extensions:
using namespace System.Security.Cryptography.X509Certificates
$caStore = [X509Store]::new([StoreName]::CertificateAuthority, [StoreLocation]::LocalMachine)
$caStore.Open([OpenFlags]::ReadOnly)
foreach ($certificate in $caStore.Certificates) {
foreach ($extension in $certificate.Extensions) {
$asnData = [System.Security.Cryptography.AsnEncodedData]::new($extension.Oid, $extension.RawData)
Write-Host "Extension Friendly Name: $($extension.Oid.FriendlyName)"
Write-Host "Extension OID: $($asnData.Oid.Value)"
Write-Host "Extension Value: $($asnData.Format($true))"
}
}
$caStore.Close()
You can specify a different store to open by specifying a different value for the [StoreName]::CertificateAuthority section.
Disclaimer, I haven't been able to test this code in production, so I'm not 100% certain that all the fields you require are exposed, but may serve as a good starting point

O365 Federation Setup - Set-MsolDomainAuthentication - "Unable to complete action. Try again later" message

I'm using Powershell to convert the O365 domain to Federated using "Set-MsolDomainAuthentication" command. When I run that command with proper parameters, I get the following error - "Set-MsolDomainAuthentication : Unable to complete this action. Try again later".
Below is the command I used -
cls
$dom = "mydomain.net"
$passiveLogOnUri = "{{...}}"
$activeLogOnUri = "{{...}}"
$entity = "wsfed-o365-idp"
$logOffUrl = "{{...}}"
$signingCert = "{{...}}"
Set-MsolDomainAuthentication -DomainName $dom -FederationBrandName $dom - Authentication Federated -PassiveLogOnUri $passiveLogOnUri -SigningCertificate $signingCert -IssuerUri $entity -ActiveLogOnUri $activeLogOnUri -LogOffUri $logOffUrl -PreferredAuthenticationProtocol "WsFed"
I waited for a few hours and tried. I still get this error. This is getting to be a block for me. Any help or suggestions would be appreciated.
**** RESOLVED *** It was an article I read in MS forums (could not find it now :( ) Basically if I want to federate O365 domains with a 3rd party Identity provider, these domains cannot share the same IssueUrl. They have to be unique. I was using the same IssuerUrI for this domain. Byt setting up another configuration and using that, I was able to make my O365 domain a federated one. Thanks to all who took time to review and comment.

Log traffic coming in to a specific URL on my Windows Server 2012 R2

I have IIS setup and hosting an application which can be contacted via an API. To connect to this API you have to send credentials to a certain URL. For example:
$data = {
username : test,
password : test
}
$url = 'https://myapi.com/api/authorize/oauth/token'
Invoke-RestMethod -Method Post -Uri $url -ContentType "application/x-www-form-urlencoded" -Headers $authHeader -Body $data
My question is, can I capture the data incoming from a certain URL, specifically the one above? I would like to get IIS or write a script to save all logs when clients contact this URL.
Right, managed to solve this.
TLDR; Add Advanced logging and filter by URL in the definition.
You need to add Advanced Logging : https://www.microsoft.com/en-us/download/details.aspx?id=7211
From here, go to your site in IIS, and select Advanced Logging
Create a new definition
Edit Filter
Add Expression
Field = URI-Stem
Operator = Equals
Value = specific URL you wish to filter by
Apply changes
Now you will have logging when a user accesses your site/API via a certain URL.
Default logging directory : C:\inetpub\logs\AdvancedLogs\DEFAULT WEB SITE
More info : https://learn.microsoft.com/en-us/iis/extensions/advanced-logging-module/advanced-logging-for-iis-log-filtering
Remember; by default the log files are owned by the SYSTEM. They need to be owned by the Administrator. To change this, right click > properties > security > advanced > Change (next to the owner) > advanced > choose Administrator (may need to authorize yourself)

How would I generate the Identity Server signing certificate

In the identity server samples we find code like this in Startup.cs
var certFile = env.ApplicationBasePath + "\\idsrv3test.pfx";
var signingCertificate = new X509Certificate2(certFile, "idsrv3test");
How would I go about replacing this for production scenarios?
For the record, the code proposed in the image posted by RuSs:
options.SigningCertificate = LoadCertificate();
public X509Certificate2 LoadCertificate()
{
string thumbPrint = "104A19DB7AEA7B438F553461D8155C65BBD6E2C0";
// Starting with the .NET Framework 4.6, X509Store implements IDisposable.
// On older .NET, store.Close should be called.
using (var store = new X509Store(StoreName.My, StoreLocation.LocalMachine))
{
store.Open(OpenFlags.ReadOnly);
var certCollection = store.Certificates.Find(X509FindType.FindByThumbprint, thumbPrint, validOnly: false);
if (certCollection.Count == 0)
throw new Exception("No certificate found containing the specified thumbprint.");
return certCollection[0];
}
}
Get a dedicated cert - either via your PKI or self-generate one:
http://brockallen.com/2015/06/01/makecert-and-creating-ssl-or-signing-certificates/
Import the key pair into the Windows certificate store, and load it from there at runtime.
To step up security, some people deploy the keys to a dedicated device (called an HSM) or to a dedicated machine (e.g. behind a firewall). The ITokenSigningService allows moving the actual token signing to that separate machine.
Recently I decided to revamp my token signing issuing process. If you're running Windows 10, you can use the awesome powershell cmdlet called New-SelfSignedCertificate.
Here is my example usage:
New-SelfSignedCertificate -Type Custom
-Subject "CN=TokenSigningForIdServer"
-TextExtension #("2.5.29.37={text}1.3.6.1.5.5.7.3.3")
-KeyUsage DigitalSignature
-KeyAlgorithm RSA
-KeyLength 2048
-CertStoreLocation "Cert:\LocalMachine\My"
Make sure you are running the command as an admin. You can obtain the certificate details by opening certlm.msc. It should be stored below Personal\Certificates.
Most of the flags should be obvious, apart from the -TextExtention one. It specifies that an Enhaced Key Usage field is set to the "Code Signing" value. You can play around with the algorithm used, key length, even add extentisons by refering to the following documentation page.
Here is how I load it from a thumbprint in my config:
Click here to see image