How to specify SAML encryption certificate in ruby-saml? - saml-2.0

The ruby-saml gem supports adding certificate and private-key info to Service Provider's metadata, but as far as I can tell, that configures only signing certificate, but not encryption one. Here's what I do:
settings.certificate = "---- BEGIN CERTIFICATE----..."
settings.private_key = "-----BEGIN PRIVATE KEY----..."
which results in the following XML section added to Service Provider's metadata:
<md:KeyDescriptor use='signing'>
<ds:KeyInfo xmlns:ds='http://www.w3.org/2000/09/xmldsig#'>
<ds:X509Data>
<ds:X509Certificate>
CERTIFICATE IS HERE GSDk3tShjl4yhShj4Hr....
</ds:X509Certificate>
</ds:X509Data>
</ds:KeyInfo>
</md:KeyDescriptor>
But how do I configure ruby-saml to add a similar section for use='encryption' in addition to use='signing'?

If I understood correctly, You are not able to send the SAMLResponse in the encrypted form. If that is the case, when you are building the SAML response, send one more parameter
saml_response = encode_response( :encryption => encryption_opts())
def encryption_opts:
return {
cert: saml_request.service_provider.cert,
block_encryption: 'aes256-cbc',
key_transport: 'rsa-oaep-mgf1p'
}

Related

SAML error "SignatureStatus: NOT_PRESENT"

UPDATE
I found that if I add a trusted cert to SPOptions.ServiceCertificates and set SPOptions.AuthenticateRequestSigningBehavior = Sustainsys.Saml2.Configuration.SigningBehavior.IfIdpWantAuthnRequestsSigned; and set IdentityProvider.WantAuthnRequestsSigned = true, the signature element is included.
Original Question:
Having troubles connecting to an IDP with the following AuthnRequest:
<saml2p:AuthnRequest xmlns:saml2p="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion" ID="idf299bf8aa08542d193e022cb047e5ecc" Version="2.0" IssueInstant="2019-07-23T00:10:13Z" Destination="https://example-idp.com" AssertionConsumerServiceURL="https://example-sp.com/Acs">
<saml2:Issuer>https://example-sp.com</saml2:Issuer>
</saml2p:AuthnRequest>
The IDP says: "SignatureStatus: NOT_PRESENT". I'm guessing that means that the authnrequest should have a <ds:Signature section? If so, how do I configure Sustainsys.Saml2.AspNetCore2 to include it?
The metadata xml I received from the idp contains a <ds:Signature section, but looking at the source code for Sustainsys.Saml2.AspNetCore2, it looks like that part of the metadata gets ignored when deserializing?
I'm not very familiar with the internals of SAML, so sorry if this is a silly question.
You'll want to generate a self-signed .pfx file that contains both your public cert and private key. We use azure key vault, but you could also use openssl. Lots of resources that explain how to generate one of those and load it into a c# X509Certificate2 instance.
Once you have an instance of X509Certificate2, set options.SPOptions.AuthenticateRequestSigningBehavior = Sustainsys.Saml2.Configuration.SigningBehavior.IfIdpWantAuthnRequestsSigned;
And set IdentityProvider.WantAuthnRequestsSigned = true.
And then add the X509Certificate2 instance like so: options.SPOptions.ServiceCertificates.Add(myX509Certificate2);
Then run your app and start the SAML SSO process. You can use hookbin or the like to see what it sends in the AuthnRequest for SAMLRequest. You can extract the xml from that by url decoding it and then base64 decoding it like so in javascript, for instance to confirm signature xml is set and correct: atob(decodeURIComponent(samlRequestValue))

User is not getting authenticated (cookies not getting set) after SAML getting processed successfully

I am using an idp initiated SSO flow. I am using Kentor.AuthServices using OWIN middleware.
Most of the flow works except, user identity is not getting SET when the control reaches my callback method after successfully processing the SAML response.
Setting in web.config:
<kentor.authServices entityId="https://one-staging.com/MVSAMLServiceProvider"
returnUrl="https://5814a15e.ngrok.io/api/Account/UnsolicitedExternalLogin">
<identityProviders>
<add entityId="https://shibidp.edu/idp/shibboleth"
metadataLocation = "~/Providers/SAML2/Metadata/shibidp.edu.xml"
allowUnsolicitedAuthnResponse="false"
disableOutboundLogoutRequests="false"
binding="HttpRedirect">
</add>
<add entityId="abb:one:saml20:idp"
metadataLocation="~/Providers/SAML2/Metadata/abb.xml"
allowUnsolicitedAuthnResponse="true"
disableOutboundLogoutRequests="false"
binding="HttpRedirect">
</add>
</identityProviders>
</kentor.authServices>
Here is my Startup.cs:
public void ConfigureOAuth(IAppBuilder app)
{
app.UseExternalSignInCookie(Microsoft.AspNet.Identity.DefaultAuthenticationTypes.ExternalCookie);
OAuthBearerOptions = new OAuthBearerAuthenticationOptions();
OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions()
{
//For Dev enviroment only (on production should be AllowInsecureHttp = false)
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/oauth2/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(30),
Provider = new CustomOAuthProvider(),
AccessTokenFormat = new CustomJwtFormat()
};
// OAuth 2.0 Bearer Access Token Generation
app.UseOAuthAuthorizationServer(OAuthServerOptions);
app.UseOAuthBearerAuthentication(OAuthBearerOptions);
googleAuthOptions = new GoogleOAuth2AuthenticationOptions()
{
ClientId = System.Configuration.ConfigurationManager.AppSettings["GoogleClientId"],
ClientSecret = System.Configuration.ConfigurationManager.AppSettings["GoogleClientSecret"],
Provider = new GoogleAuthProvider()
};
app.UseGoogleAuthentication(googleAuthOptions);
app.Use(async (Context, next) =>{await next.Invoke();});
app.UseKentorAuthServicesAuthentication(CreateSAMLAuthServicesOptions());
app.Use(async (Context, next) =>{await next.Invoke();});
}
Here are the Kentor logs (no errors in the logs):
DEBUG 2018-12-28 14:02:32,682 8859ms emv-authService-logger MoveNext - Received unsolicited Saml Response _t0r6DHtsGygxkYcfNzdkEs72.M which is allowed for idp abb:one:saml20:idp
DEBUG 2018-12-28 14:02:32,729 8906ms emv-authService-logger MoveNext - Signature validation passed for Saml Response _t0r6DHtsGygxkYcfNzdkEs72.M
DEBUG 2018-12-28 14:02:32,729 8906ms emv-authService-logger MoveNext - Extracted SAML assertion oN4v.k9x2GE7s5S8OdeNWS.93j9
DEBUG 2018-12-28 14:02:32,729 8906ms emv-authService-logger MoveNext - Validated conditions for SAML2 Response _t0r6DHtsGygxkYcfNzdkEs72.M
INFO 2018-12-28 14:02:32,729 8906ms emv-authService-logger ProcessResponse - Successfully processed SAML response _t0r6DHtsGygxkYcfNzdkEs72.M and authenticated 10035094
Finally my redirect method:
[OverrideAuthentication]
[HostAuthentication(DefaultAuthenticationTypes.ApplicationCookie)]
[AllowAnonymous]
[Route("UnsolicitedExternalLogin", Name = "UnsolicitedExternalLogin")]
public async void GetUnsolicitedExternalLogin()
{
bool isAuthenticated = User.Identity.IsAuthenticated; //getting false
}
I have unfortunately been stuck with this problem for a week now. I'm sure this is really close to getting done, so any help would be greatly appreciated.
Thanks!
Looking at the code, I think that there is a mismatch on authentication schemes.
In the pipeline setup, a cookie middleware for the external authentication scheme is setup. But in the GetUnsolicitedExternalLogin method, the ApplicationCookie scheme is referenced. Change it to reference the external scheme instead.
It is also a good idea to check if the redirect from ~/AuthServices/Acs to GetUnsolicitedExternalLogin sets an external authentication cookie.

Getting Claims from ADFS 3.0 and Displaying To Shibboleth 2.6 Service Provider (SP) in SAML 2.0

I'm trying to configure Windows Server 2012 r2 ADFS 3.0 to send the NameID to Shibboleth 2.6 SAML 2.0 SP, but I keep on getting these errors:
2016-11-15 10:07:07 WARN Shibboleth.AttributeResolver.Query [1]: can't attempt attribute query, either no NameID or no metadata to use
2016-11-15 10:07:07 INFO Shibboleth.SessionCache [1]: new session created: ID (_7e425978e43bc32c86393f518b26eb3e) IdP (https://c-adfs01.contoso.com/FederationMetadata/2007-06/FederationMetadata.xml) Protocol(urn:oasis:names:tc:SAML:2.0:protocol) Address (192.168.50.131)
I understand that this has something to do about passing the NameID attribute from the ADFS IDP and displaying the nameID in Shibboleth SAML 2.0 SP.
I've also read that you need to set a rule in ADFS IDP to pass the Email Address, and transform the Email address to become a NameID. I have done that, and have these rules:
c:[Type == "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"]
=> issue(Type = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier", Issuer = c.Issuer, OriginalIssuer = c.OriginalIssuer, Value = c.Value, ValueType = c.ValueType, Properties["http://schemas.xmlsoap.org/ws/2005/05/identity/claimproperties/format"] = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress");
However, I have observed that the configuration seems compatible only with SAML 1.0, but not 2.0 (maybe).
So, I went on by adding the name attributes. I tried transient, persistent, and emailAddress, and trying both SAML 1.0 and SAML 2.0 configurations.
<Attribute name="urn:oasis:names:tc:SAML:2.0:nameid-format:transient" id="NameID"/>
<Attribute name="urn:oasis:names:tc:SAML:2.0:nameid-format:persistent" id="NameID"/>
<Attribute name="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress" id="NameID"/>
Question:
So, what is the proper way of configuring ADFS IDP to send the NameID to the Shibboleth SP and display the Session attribute when going to my application Session page?
Specifically, what is the proper output for Claim Rules that I should expect? and how should the XML configuration for Shibboleth attribute-map.xml look like?
https://c-app01.contoso.com/Shibboleth.sso/Session
Solution:
The solution is to create two rules inside "Issuance Transform Rules" tab in the Relying Party. Here are the Rules to be set:
Add a Rule to Send LDAP Attributes as Claims
Add a Rule to Transform an Incoming Claim
Also, make sure that Permit Access to All Users is inside Issuance Authorization Rules tab.
Instructions:
Add a Rule to Send LDAP Attributes as Claims:
Claim Rule Name: Email
Attribute Store: Active Directory
Mapping of LDAP: E-mail-Address to E-mail Address
This will map the Email address.
Add a rule to Transform An Incoming Claim:
Claim rule name: EmailToNameID
Incoming Claim Type: E-Mail Address
Outgoing Claim Type: Name ID
Outgoing Name ID Format: Email
Make sure "Pass Through Claim Values" is selected.
The setting above for "Transform An Incoming Claim" will give you the following claim rule:
c:[Type == "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"]
=> issue(Type = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier", Issuer = c.Issuer, OriginalIssuer = c.OriginalIssuer, Value = c.Value, ValueType = c.ValueType, Properties["http://schemas.xmlsoap.org/ws/2005/05/identity/claimproperties/format"] = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress");
After that, you may configure the attribute-map.xml like so:
<Attribute name="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress" id="Email"/>
My Mistake:
I found out that every setting in my Claims Rules is correct, except that the rules for Email and EmailToNameID should be under the Issuance Transform Rules Tab of the Relying Party.

How to configure IdentityServerAuthenticationOptions.Authority to use wildcards

I successfully setup IdentityServer4 with ASP.NET Core.
As a default config I had this:
IdentityServerAuthenticationOptions options = new IdentityServerAuthenticationOptions()
{
Authority = "http://localhost:5000",
ScopeName = "scope",
ScopeSecret = "ScopeSecret",
AutomaticAuthenticate = true,
AutomaticChallenge = true,
RequireHttpsMetadata = false,
};
Now, using this guide I configured to be read from configuration files and so they can be any numbers in production.
For example if I setup API to be running at http://*:5000 then the client can connect to it via the service IP address like http://192.168.1.100:5000.
Once the client obtains the Bearer token and tries to use it, an Internal Server Error occures with this exception:
Unable to obtain configuration from:
'http://*:5000/.well-known/openid-configuration'.
---> System.IO.IOException: IDX10804: Unable to retrieve document from: 'http://*:5000/.well-known/openid-configuration'.
---> System.UriFormatException: Invalid URI: The hostname could not be parsed.
What is the correct way to configure IdS4 to have dynamic authority?
Update
It seems the problem is with Issuer, any idea on this?
Microsoft.IdentityModel.Tokens.SecurityTokenInvalidIssuerException:
IDX10205: Issuer validation failed. Issuer: 'http://192.168.1.100:5000'. Did not match: validationParameters.ValidIssuer: 'http://localhost:5000' or validationParameters.ValidIssuers: 'null'.
at Microsoft.IdentityModel.Tokens.Validators.ValidateIssuer(String issuer, SecurityToken securityToken, TokenValidationParameters validationParameters)
By a big surprise, all I needed, was to set a value (almost any value) for IssuerUri:
public IServiceProvider ConfigureServices(IServiceCollection services)
{
////...
var identiyBuilder = services.AddIdentityServer(options =>
{
options.RequireSsl = false;
options.IssuerUri = "MyCompany";
});
////...
}
Now, by the above config, I can use the service by any IP address.
I didn't find I could just put in MyCompany
But in my log files I had the following:
Bearer was not authenticated. Failure message: IDX10205: Issuer validation failed. Issuer: 'https://crm.example.com'. Did not match: validationParameters.ValidIssuer: 'MyCompany' or validationParameters.ValidIssuers: 'null'.
I don't quite know what 'issuer' means but I was able to just take 'https://crm.example.com' and get things working with this :
options.IssuerUri = "https://crm.example.com";

Interface Between Google Sign-in and MailKit

I am writing an app in WPF (Windows 10 desktop) that should
include a component where the user can download message headers
and messages from G-Mail.
I am trying to use MailKit to interface with G-Mail via a secure
connection (without having to turn on "allow less-secure apps"
for G-Mail) and download messages with POP3. I am a bit confused
as to the proper procedure.
FYI: I know next to nothing about OAuth and TLS, so KISS please.
I have created and downloaded a JSON file for OAuth 2.0 from Google.
I have visited the FAQ for MailKit, and the following section
seems relevant, but I'm not sure as to what I should plug in
to the interface.
(Please see the code below.)
For "password", would that be the password for the account?
I'm not sure as to what to give for
"your-developer-id#developer.gserviceaccount.com".
.........................................................
https://github.com/jstedfast/MailKit/blob/master/FAQ.md#ProtocolLog
.........................................................
From the Q & A:
How can I log in to a GMail account using OAuth 2.0?
The first thing you need to do is follow Google's instructions for
obtaining OAuth 2.0 credentials for your application.
Once you've done that, the easiest way to obtain an access token is to
use Google's Google.Apis.Auth library:
var certificate = new X509Certificate2 (#"C:\path\to\certificate.p12", "password",
X509KeyStorageFlags.Exportable);
var credential = new ServiceAccountCredential (new ServiceAccountCredential
.Initializer ("your-developer-id#developer.gserviceaccount.com") {
// Note: other scopes can be found here: [links]
Scopes = new[] { "https mail google com " },
User = "username#gmail.com"
}.FromCertificate (certificate));
bool result = await credential.RequestAccessTokenAsync (CancellationToken.None);
// Note: result will be true if the access token was received successfully
// Now that you have an access token (credential.Token.AccessToken), you can
// use it with MailKit as if it were the password:
using (var client = new ImapClient ()) {
client.Connect ("imap.gmail.com", 993, true);
// use the access token as the password string
client.Authenticate ("username#gmail.com", credential.Token.AccessToken);
}
My next question: Would the user be able to access their own account(s)
with my app without having to follow the same procedure?
IOW: Will the credentials that I've downloaded work for any account?
... or allow access only to the account from which the credentials
were created?
If the credentials are only good for my own account, then I'll have to
do something else.
Would Google Sign-In be a better approach?
I've downloaded the example code for .NET from Google:
https://github.com/googlesamples/oauth-apps-for-windows
I've built and ran ran "OAuthConsoleApp", as well as "OAuthDesktopApp".
It would seem that I am getting a secure connection from those,
as I have gotten the following output:
.........................................................
redirect URI: http 127.0.0.1:64003
Listening..
Authorization code: qwerty ...
Exchanging code for tokens...
Send the request ...
GetRequestStream ...
await stream.WriteAsync ...
Get the response ...
responseText ...
{
"access_token": "qwerty ...",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "qwerty ...",
"id_token": "qwerty ..."
}
Making API Call to Userinfo...
+++ userinfoResponseText : {
"sub": "117108120545711995673",
"name": "My Name",
"given_name": "My",
"family_name": "Name",
"picture": "qwerty ...",
"locale": "en"
}
.....................................................
I see that I have an "access_token" in the response and I thought
that I could plug that in to the "client.Authenticate" method for
MailKit as the password (as mentioned in the docs for MailKit):
string access_token = tokenEndpointDecoded["access_token"];
client.Connect ("pop.gmail.com", 995, SecureSocketOptions.SslOnConnect);
client.Authenticate ("username#gmail.com", access_token);
It threw an exception:
.....................................................
"POP3 server did not respond with a +OK response to the AUTH command."
at MailKit.Net.Pop3.Pop3Client.Authenticate(Encoding encoding,
ICredentials credentials, CancellationToken cancellationToken)
at MailKit.MailService.Authenticate(String userName, String
password, CancellationToken cancellationToken)
at
NS_MailKit_01.Pop3.cls_mailKit_Pop3_01.connect_and_authenticate(Object
p3_client, String p_access_token)
in :\Software_Develpoment_Sys_03_K\MIME_EMail\TEST_02\Mail_Kit_01\MailKit_01.cs:line
465
at
LIB1_01_G_Mail_Auth.cls_G_mail_authorization.str_token_NTRF.invoke_access_token(String
p_access_token)
in K:\Software_Develpoment_Sys_03_K\MIME_EMail\TEST_02\OAuth\oauth-apps-for-windows\OAuthConsoleApp\LIB1_01_G_Mail_Auth\G_Mail_Auth_01.cs:
line 95
at
LIB1_01_G_Mail_Auth.cls_G_mail_authorization.d__13.MoveNext()
in K:\Software_Develpoment_Sys_03_K\MIME_EMail\TEST_02\OAuth\oauth-apps-for-windows\OAuthConsoleApp\LIB1_01_G_Mail_Auth\G_Mail_Auth_01.cs:line
343
.....................................................
Does anyone know how I could get a "credential" object from
the Google interface that I could use with MailKit?
Any help would be appreciated.
Thanks!
For "password", would that be the password for the account?
No. It would be the password for your PKCS12 file containing your X.509 Certificate and your private key.
I'm not sure as to what to give for "your-developer-id#developer.gserviceaccount.com".
You need to register yourself and your application with Google's Developer program which will give you a developer id to use. You need to follow their directions.

Resources