Saturday, December 19, 2015

Office 365 SAML 2.0 Federation with WSO2 Identity Server


Office 365 basically support three identity models [1].

Cloud Identity:
Here user identity is managed in Azure Active Directory (Cloud directory used by Office 365)

Synchronized Identity:
Here user identity is managed in an on-premises server and the user accounts and password hashes are synchronized with Azure Active Directory

Federated Identity:
Here user identity is verified by the on-premises Identity Provider (IdP). Yet user accounts should be synchronized with Azure AD except the password hashes, so that the user authentication happens via the on-premises IdP.

So here, I'm going to explain about configuring the 'Federated Identity' model with WSO2 Identity Server with SAML 2.0.

So, before starting make sure that you have below.

  • Office 365 Business Account with access to Admin Portal (Here I'm using a 30 day trial business account.)
  • Internet-resolvable domain name (Office 365 SSO requires an Internet-resolvable domain name to use as the suffix in each user’s username. You cannot federate the default domain that is provided by Microsoft that ends with "onmicrosoft.com").
  • A Windows Platform with Windows Azure Active Directory Powershell installed. You can get it from here.
  • WSO2 Identity Server 5.1.0 (You can get the latest releases of WSO2 Identity Server from here)

So first lets see how we can configure Azure Active Directory to trust our on-premises IdP which is the WSO2 Identity Server in this case.


1. Start Widows Azure Active Directory Powershell.


2.  Run $cred=Get-Credential
     This will prompt for windows Azure AD Admin credentials.



3. Run Connect-MsolService –Credential $cred to connect with stored credentials













4. To verify the availability of validated domain, run Get-MsolDomain . The ‘Status’ of our domain should be ‘Verified’, and ‘Authentication’ should be ‘Managed’.












5. Configure the domain as a federated domain, providing respective federation settings that match the IdP. Let's store federation settings in parameters.

Store your domain
$dom = "malithimal.com"

IdP Logon URL to POST SAML Authentication Request. Here it's the SAML SSO endpoint URL of WSO2 IS.
$logonurl = "https://localhost:9443/samlsso"

Issuer Id of the IdP. This should be the “Identity Provider Entity Id” in Resident IDP of WSO2 IS with regard to SAML.
$issueruri = "wso2is.510"

IdP Logout URL to POST SAML Logout Request. So it's the SAML SSO endpoint URL of WSO2 IS.
$logouturl = "https://localhost:9443/samlsso"

IdP Certificate. Base64 encoded signing certificate of WSO2 IS should be given here. For default key store configured in WSO2 IS certificate is as below. Please note that the certificate should be given in a single line without a break.
$cert = "MIICNTCCAZ6gAwIBAgIES343gjANBgkqhkiG9w0BAQUFADBVMQswCQYDVQQGEwJVUzELMAkGA1UECAwCQ0ExFjAUBgNVBAcMDU1vdW50YWluIFZpZXcxDTALBgNVBAoMBFdTTzIxEjAQBgNVBAMMCWxvY2FsaG9zdDAeFw0xMDAyMTkwNzAyMjZaFw0zNTAyMTMwNzAyMjZaMFUxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDQTEWMBQGA1UEBwwNTW91bnRhaW4gVmlldzENMAsGA1UECgwEV1NPMjESMBAGA1UEAwwJbG9jYWxob3N0MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCUp/oV1vWc8/TkQSiAvTousMzOM4asB2iltr2QKozni5aVFu818MpOLZIr8LMnTzWllJvvaA5RAAdpbECb+48FjbBe0hseUdN5HpwvnH/DW8ZccGvk53I6Orq7hLCv1ZHtuOCokghz/ATrhyPq+QktMfXnRS4HrKGJTzxaCcU7OQIDAQABoxIwEDAOBgNVHQ8BAf8EBAMCBPAwDQYJKoZIhvcNAQEFBQADgYEAW5wPR7cr1LAdq+IrR44iQlRG5ITCZXY9hI0PygLP2rHANh+PYfTmxbuOnykNGyhM6FjFLbW2uZHQTY1jMrPprjOrmyK5sjJRO4d1DeGHT/YnIjs9JogRKv4XHECwLtIVdAbIdWHEtVZJyMSktcyysFcvuhPQK8Qc/E/Wq8uHSCo="

6. Run below to establish the trust
Set-MsolDomainAuthentication –DomainName $dom -Authentication Federated -PassiveLogOnUri $logonurl -SigningCertificate $cert -IssuerUri $issueruri -LogOffUri $logouturl -PreferredAuthenticationProtocol SAMLP













7. To verify federation settings run Get-MsolDomainFederationSettings -Domain $dom













So now we have setup the trust between our on-premises IdP WSO2 Identity Server and Azure Active Directory.

Note:
In case you need to redo configurations first you will need to move your domain back to managed authentication mode as below.

Set-MsolDomainAuthentication -DomainName $dom -Authentication Managed

Then redo the settings by providing parameters as in step (5) and setting the authentication method again as in step (6)


So let's move on to configuring WSO2 Identity Server.


So I have configured an Active Directory User Store as my primary User Store in my local server.
You can refer [2] in order to configure an Active Directory User Store.

Basically, there are three attributes that Azure AD expects in a SAML 2.0 message.


NameID The value of this assertion must be the same as the Azure AD user’s ImmutableID. This is the subject in the SAML response to Azure AD. So in this case we will use the ObjectGUID attribute in AD which is unique per user
IDPEmailThe User Principal Name (UPN) is listed in the SAML response as an attribute element with the name IDPEmail This is the user’s UserPrincipalName (UPN) in Windows Azure AD/Office 365. Basically this is the login username that a user tries out to login for Office 365. It should match with the domain name. (ex: malithi@malithimal.com). So here we will use the userPrincipalName attribute in AD to store this value
IssuerThis is required to be a URI of the Identity Provider

So from above three let's first configure the IdP Issuer Id. This should be the same value that you have provided as IssuerUri in step (5) and (6) when configuring Azure AD.

1. Start up WSO2 IS, running below from the console at path /repository/bin
    (I'm working on Unix so executing the shell command)
    sh wso2server.sh

2. Login to the Management Console invoking 'https://localhost:9443/carbon/'

3. Go to the 'Resident Identity Provider > Inbound Authentication Configuration > SAML2 Web SSO Configuration' and replace the value of 'Identity Provider Entity Id' with the value given for param '$issueruri' in step (5) when configuring Azure AD















4.  Add below two claims by going to 'Claims > Add > Add New Claim' under ‘http://wso2.org/claims’ dialect to include 'NameID' and 'IDPEmail' attributes in the SAML response as expected by Azure AD







Note:
Please un tick the 'Supported by Default' checkbox of below Claims under ‘http://wso2.org/claims’ dialect. These attributes are not supported by Active Directory by default. Either we have to map them to a proper attribute in Active Directory or need to add the mapped attributes. Since these are ticked as 'Supported by Default' these attributes are shown in the default user profile and we will get an error once we try to update the profile.


ClaimClaim UriMapped Attribute
Countryhttp://wso2.org/claims/country country
Organizationhttp://wso2.org/claims/organization organizationName
IMhttp://wso2.org/claims/im im


5. Go to 'Service Providers > Add' and add a new Service Provider as 'Office365'















6.  Go to 'Inbound Authentication Configuration > SAML2 Web SSO Configuration > Configure' and configure SAML2 Settings as below.


AttributeValue
Issuerurn:federation:MicrosoftOnline
Assertion Consumer URLshttps://login.microsoftonline.com/login.srf
NameID formaturn:oasis:names:tc:SAML:2.0:nameid-format:persistent
Enable Response SigningTicked (True)
Enable Attribute ProfileTicked (True)
Include Attributes in the Response AlwaysTicked (True)


















7. Configure the attributes required by Azure AD

Service Provider ClaimLocal ClaimRequested Claim
IDPEmailhttp://wso2.org/claims/upnTicked (True)
NameIDhttp://wso2.org/claims/objectguidTicked (True)
















Make sure to set the configured 'NameID' claim as the 'Subject Claim URI'


Ok. So we have configured Office 365 as a Service Provider in WSO2 IS.
Now we need to have a user to test this and that user should be synced with Azure AD.

So I have a user as 'maltestuser1' in WSO2 IS and I have updated the user profile as below to have a UserPrincipalName.














Note:
ObjectUUID is a binary attribute. So in order to see the value properly in the management console we have to add the below user store property to user store configuration at /repository/conf/user-mgt.xml.

<UserStoreManager ... >
...
<Property name="java.naming.ldap.attributes.binary">objectGUID</Property>
...
</UserStoreManager>


So as I have mentioned in the very beginning, federation eliminates the need to send passwords between Active Directory and Office 365, yet, it still requires synchronizing the user accounts with Azure AD. We can perform this synchronization manually, adding Office 365 users that match each Active Directory user account or we can also automate the process with the Microsoft Directory Synchronization Tool

So here, we will manually sync this user with Azure AD. 

1. Connect with Windows Azure AD Powershell module executing step (1), (2) and (3) followed when configuring Azure AD.

2. Run below
New-MsolUser -UserPrincipalName maltestuser1@malithimal.com -ImmutableID jSxdNXJhcUSqepFsYn495Q== -LastName maltestuser1 -FirstName maltestuser1 -DisplayName "Malithi's Test User 1"













Note:
Make sure to use the value specified under objectGUID as -ImmutableId and the value specified under userPrincipalName,  as the UserPrincipalName.


If you log on to the Office 365 Admin Portal you will now see the newly added user there.
















Ok. So let's logon to Office 365 with the user created.


1. Go to https://login.microsoftonline.com/

2. Enter the username with federated domain (This is the value given for UserPrincipalName)
In my case it's maltestuser1@malithimal.com

3. You will be redirected to the login page of the WSO2 Identity Server

4. Give user credentials these and log on.

5. So you will be successfully logged on to Office 365.

6. If you signed out from Office 365, WSO2 IS will receive a SAML Logout Request and the user will be signed out from the IdP.

So that's it :)

[1] https://blogs.office.com/2014/05/13/choosing-a-sign-in-model-for-office-365/
[2] https://docs.wso2.com/display/IS500/Configuring+an+Active+Directory+User+Store

Sunday, June 7, 2015

Public Key Cryptography in PHP

When we are dealing with Java, when it comes to cryptography the most basic thing that we come across is the 'keystore'. It's a kind of key database that save and manage SecretKey, Public/Private Key Pair, and certificates. It's simply a password protected file.
When it comes to PHP, I couldn't find such 'keystore' concept used for Key Management. Instead what I found was the OpenSSL PHP extension [1] that supports the standard OpenSSL cryptography toolkit [2] and some other libraries that tries to reinvent the wheel.

However, referring the OpenSSL PHP extension manual and surfing the web confirmed that it's fairly easy to use this extension to secure a system. Let's take one example and explore the extension for a bit.

Lets' look at an Asymmetric Encryption scenario. So we will need a private key and a public key, where the private key is a secret and the public key can be given to anyone who want to communicate with us. A person who want to send us some data securely will encrypt with our public key, so only we will be able to decrypt it with our private key.

So first we need to generate the public and private key. This can be done either by using the openssl command line tool or the extension. Make sure to password protect the file or the string you generate giving the passphrase in the extension or via commandline tool. Otherwise the private key is not encrypted and it is completely unprotected.
In the tool you can specify the passphrase with argument -passout

Using command line tool:


openssl genrsa -des3 -passout pass:password -out private.pem  1024

This will generate a 1024 bit rsa private key, which is password protected by password 'password'.

However, here the passphrase could be grabbed by any other malicious process, since command line arguments are generally visible to all processes.
A better option would be to write passphrase to a file with protected permissions and use that,


openssl genrsa -des3 -passout file:passphrase.txt -out private.pem  1024

or provide it in standard input.


openssl genrsa -des3 -passout stdin -out private.pem  1024

Using PHP OpenSSL extension:


$passphrase = "password";
$privateKey = openssl_pkey_new(array(
 'private_key_bits' => 1024,
 'private_key_type' => OPENSSL_KEYTYPE_RSA,
));
openssl_pkey_export_to_file($privateKey, '/path/to/privatekey', $passphrase);
 
// get the public key $keyDetails['key'] from the private key;
$keyDetails = openssl_pkey_get_details($privateKey);
file_put_contents('/path/to/publickey', $keyDetails['key']);
 

Then we can load the public key and encrypt data as below.


$pubKey = openssl_pkey_get_public('file:///path/to/publickey');
openssl_public_encrypt($plainText, $encryptedText, $pubKey);

From the private key we can decrypt the encrypted data and get the plain text again.


$privateKey = openssl_pkey_get_private('file:///path/to/privatekey', $passphrase);
openssl_private_decrypt($encryptedText, $plainText, $privateKey);

Likewise we can use PHP OpenSSL extension to sign or seal as well. Please refer the documentation [1] for more details and you can also find a bunch of examples contributed by users also.

[1] http://php.net/manual/en/book.openssl.php
[2] https://www.openssl.org/docs/apps/openssl.html

Saturday, June 6, 2015

How to enable Apache & PHP on Mac OS X Yosemite

Apache and PHP are packaged with OS X. So we will just have to enable them in order to create a local web server.

First open terminal and switch to the root user to avoid permission problems

sudo su

Starting Apache server


Run following command from the terminal

apachectl start

This will start the apache server and you can verify it by invoking following url from the browser

http://localhost

You will see a page showing 'It works!'

Enable PHP


Backup the default apache configuration at /etc/apache2 in any case you might need it :)

cd /etc/apache2/
cp httpd.conf httpd.conf.bak

Now we have to edit the configuration file

vi httpd.conf

To enable PHP we have to uncomment the following line in httpd.conf by removing #

LoadModule php5_module libexec/apache2/libphp5.so

Restart Apache server

apachectl restart

Now we can verify if PHP is enabled. In order to do that let's create a page that calls phpinfo() in the DocumentRoot.

The default DocumentRoot for Mac OS X is /Library/WebServer/Documents
You can see that configuration in httpd.conf

grep DocumentRoot httpd.conf

Now create the phpinfo() page

echo '<? php phpinfo();' > /Library/WebServer/Documents/phpinfo.php

Now we can verify PHP by invoking http://localhost/phpinfo.php

Monday, March 2, 2015

How to manage Session in WSO2 IS 5.0.0 + SP01


Please note that the configurations and the post below applies only for WSO2 IS 5.0.0 + SP01

In authentication framework logged in session for a user is maintained with the commonAuthId session cookie in the SessionContextCache(AppAuthFrameworkSessionContextCache).
By default no expiration time is set to the commonAuthId cookie and the cache gets invalidated by 15 minutes which is the default cache invalidate time. This cache expiary time is hard coded in org.wso2.carbon.caching.impl.CacheImpl. Thus,
it's not configurable at the moment.
So this makes the idle timeout for the logged in session as 15 minutes which is not configurable.
Further, following configurations are available to manage logged in session timeout in identity.xml under Server.JDBCPersistenceManager configuration block.
We can enable session data persistence with below.

<SessionDataPersist>
<...>...<...>
<Enable>true</Enable>
<...>...<...>
</SessionDataPersist>

This indicates to store session data associated with the logged in session.
Session data persistence comes with a cleanup service configuration that removes stale sessions.

<SessionDataPersist>
<...>...<...>
<CleanUp>
<Enable>true</Enable>
<Period>10</Period>
<TimeOut>60</TimeOut>
</CleanUp>
<...>...<...>
</SessionDataPersist>


Cleanup service gets executed only if it is enabled with SessionDataPersist.CleanUp.Enable.
SessionDataPersist.CleanUp.Period defines the time period among two consecutive cleanups in minutes. By default it is 1 day.
SessionDataPersist.CleanUp.TimeOut defines the timeout value of session data in minutes. By default it is two weeks.
For an example if we consider the above configuration it means that the clean up task will run periodically with a period of 10 minutes.
And in a cleanup process it will remove all session data persisted before 60 minutes.

Remember me time period can be configured as below.

<SessionDataPersist>
<...>...<...>
<RememberMePeriod>60</RememberMePeriod>
<...>...<...>
</SessionDataPersist>


Configuring above will set the expire time for the commonAuthId cookie only if remember me option is selected when user logs in. So as per above configuration the expiration time for the commonAuthId cookie is 60 minutes.
By default if remember me option is selected the cookie expiration time is set to two weeks.
Thus, the browser will expire the cookie after this much of time.
Further, if the logged in session is invalidated from the cache, it will be restored back to the cache, if and only if the remember me option is selected. Thus, no matter that session data persistence is enabled if remember me option is not selected the idle time out of the logged in session becomes 15 minutes which is the default cache invalidate time.

In addition below configurations are possible as well under <SessionDataPersist>.

SessionDataPersist.Only
Setting this to true will disable caching and session data will only be persisted. Thus, session data will be available only if the user has selected remember me option when login. Otherwise even though session data is persisted it's not retrieved.
This also disables caching in AthenticationContextCache and AuthenticationResultCache as well.

SessionDataPersist.Temporary
Setting this property to true will store data added to AuthenticationContextCache and AuthenticationResultCache as well.
In a cache hit if entry is not found data will be retrieved from the session persistence store

I will update the post on how to test session timeout later. :)

Sunday, March 1, 2015

How to solve conflicts in Git Pull Requests

Github allows people to collaborate and be more productive. One such way of collaborating is Pull Requests. It let us to tell others about that changes that we want to merge for a specific repository on Github.
The recommended way is to first fork the repo that you want to contribute. Then clone the forked repo. Create a branch and add commits to that branch. Then push your commits to the remote repo that you forked. If there are changes between our branch and upstream branch Github allows us to open a pull request.

You can find more information about forking, cloning, branching etc. from the Github documentation .

Here, I'm going a one step ahead.
Sometimes when you have created a pull request Github will report that there are merge conflicts in the pull request that you created. This happens because in both the upstream and your branch same files have been changed in the same point making it difficult for Github to merge by itself. In this case we have to resolve the conflicts manually.

It's just a matter of following steps below. :)

1. First fetch the upstream
git fetch upstream

2. Switch to the master branch
git checkout master

3. Merge the fetched upstream to the master branch
git merge upstream/master

4. Switch to the branch that you did developing stuff
git checkout <your_branch>

5. Merge the master with your branch
git merge master

Here you will see if there are merge conflicts

6. Solve the conflicts with git mergetool
We can solve the conflicts with Meld
For that we just have to give the tool type to the git mergetool command as below.
git mergetool -t meld

This will open up the merge window with three columns.
The left most shows your local working copy as LOCAL.
The right most shows the remote branch change to be merged as REMOTE.
The middle one is the half finished merge which is the BASE. This is one which is finally going to be saved. So you have to merge changes from the LOCAL and the REMOTE to the BASE.

7. Then commit all changes.
git commit -m "Commit message"

8. Finally push commits to your remote repo.
git push origin <your_branch>

Hope this would help.