4. Authenticating Accounts with Stormpath

Authentication is the process by which a system identifies that someone is who they say they are. Perhaps the most accessible example of this process is at the airport, where you must present your passport and your plane ticket. The passport is used to authenticate you, that you are who you present yourself to be, and the plane ticket represents your authorization to board a specific flight.

In this chapter you will cover three of the ways that Stormpath allows you to authenticate users: password authentication, token authentication, and social authentication.

4.1. How Password Authentication Works in Stormpath

Probably the single most common way of authenticating a user is to ask them for their account credentials. When a user creates an Account in Stormpath, it is required that they provide a username (or email) and a password. Those credentials can then be provided in order to authenticate that Account.

4.1.1. Authenticating An Account

After an Account resource has been created, you can authenticate it given an input of a username/email and a password from the end-user. When authentication occurs, you are authenticating an Account within a specific Application against that Application’s Organizations, Directories and Groups (more on that below). The key point is that the Application resource is the starting point for authentication attempts.

Once you have the Application resource you may attempt authentication by sending a POST request to the Application’s /loginAttempts endpoint and providing a base64 encoded username/email and password pair that is separated with a colon (for example testuser:testpassword). Stormpath requires that the username/email and password are base64 encoded so that these values are not passed as clear text. For more information about the /loginAttempts endpoint please see the Reference Chapter.

So, if you had a user Account “Han Solo” in the “Captains” Directory, and you wanted to log him in, you would first need to take the combination of his username and password (“first2shoot:Change+me1”) and then Base64 encode them: Zmlyc3Qyc2hvb3Q6Q2hhbmdlK21lMQ==.

You would issue the following POST to your Application with ID 1gk4Dxzi6o4PbdleXaMPLE:

POST /v1/applications/1gk4Dxzi6o4PbdleXaMPLE/loginAttempts HTTP/1.1
Host: api.stormpath.com
Authorization: Basic MlpG...
Content-Type: application/json;charset=UTF-8

{
  "type": "basic",
  "value": "Zmlyc3Qyc2hvb3Q6Q2hhbmdlK21lMQ==",
  "accountStore": {
     "href": "https://api.stormpath.com/v1/directories/2SKhstu8PlaekcaEXampLE"
   }
}

You are using the Base64 encoded value from above, and (optionally) specifying that the Account can be found in the “Captains” Directory from earlier.

Note

It is also possible to specify a Group or Organization’s href or an Organization’s nameKey instead of an Directory’s. Passing a nameKey would look like this:

POST /v1/applications/1gk4Dxzi6o4PbdleXaMPLE/loginAttempts HTTP/1.1
Host: api.stormpath.com
Authorization: Basic MlpG...
Content-Type: application/json

{
  "type": "basic",
  "value": "YWxhbkBzbWl0aGVlZS5jb206UGFzcexample",
  "accountStore": {
    "nameKey":"anOrgNameKey"
  }
}

On success you would get back the href for the “Han Solo” Account:

HTTP/1.1 200 OK
Location: https://api.stormpath.com/v1/accounts/72EaYgOaq8lwTFHILydAid
Content-Type: application/json;charset=UTF-8

{
  "account": {
    "href": "https://api.stormpath.com/v1/accounts/72EaYgOaq8lwTFHILydAid"
  }
}

The reason this succeeds is because there is an existing Account Store Mapping between the “Han Solo” Account’s “Captains” Directory and your Application. This mapping is what allows this Account to log in to the Application.

Note

Instead of just receiving an Account’s href after successful authentication, it is possible to receive the full Account resource in the JSON response body. To do this, simply add the expand=account parameter to the end of your authentication query:

https://api.stormpath.com/v1/applications/$YOUR_APPLICATION_ID/loginAttempts?expand=account

For more information about link expansion, please see the Reference chapter.

4.1.2. How Login Attempts Work in Stormpath

When the “Han Solo” Account tries to log in to the Application, the user submits a request to the Application’s /loginAttempts endpoint. Stormpath then consults the Application’s assigned Account Stores (Organizations, Directories, and Groups) in the order that they are assigned to the Application. When a matching Account is discovered in a mapped Account Store, it is used to verify the authentication attempt and all subsequent Account Stores are ignored. In other words, Accounts are matched for Application login based on a “first match wins” policy.

Let’s look at an example to illustrate this behavior. Assume that two Account Stores, a “Customers” Directory and an “Employees” Directory, have been assigned (mapped) to a “Foo” application. “Customers” was assigned first, and “Employees” was assigned next, and this will dictate the order in which they are checked.

The following flow chart shows what happens when an Account attempts to log in to the Foo application:

Login Attempt Flow

The Login Attempt Flow

As you can see, Stormpath tries to find the Account in the “Customers” Directory first because it has a higher priority than the “Employees” directory. If not found, the “Employees” Directory is tried next as it has a lower priority.

You can map multiple Account Stores to an Application, but only one is required to enable login for an Application. Mapping multiple Account Stores to an Application, as well as configuring their priority, allows you precise control over the Account populations that may log in to your Application.

4.1.3. Manage Who Can Log Into Your Application

As is hopefully evident by now, controlling which Accounts can log in to your Application is largely a matter of manipulating the Application’s Account Store Mappings.

For more detailed information about this resource, please see the Account Store Mapping section of the Reference chapter.

The reason why your user “Han Solo” was able to log in to your application is because the Application resource that represents your application and your “Captains” Directory are mapped to one another by an Account Store Mapping.

You can find this mapping by sending a GET to your Application’s /accountStoreMappings endpoint, which would yield the following response:

HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8

{
  "href":"https://api.stormpath.com/v1/applications/1gk4Dxzi6o4PbdleXaMPLE/accountStoreMappings",
  "offset":0,
  "limit":25,
  "size":1,
  "items":[
    {
      "href":"https://api.stormpath.com/v1/accountStoreMappings/5WKhSDXNR8Wiksjv808XHp",
      "listIndex":1,
      "isDefaultAccountStore":true,
      "isDefaultGroupStore":true,
      "application":{
        "href":"https://api.stormpath.com/v1/applications/1gk4Dxzi6o4PbdleXaMPLE"
      },
      "accountStore":{
        "href":"https://api.stormpath.com/v1/directories/2SKhstu8Plaekcai8lghrp"
      }
    }
  ]
}

Note

Any new Accounts and Groups added to this Application via it’s /accounts and /groups endpoints will be added to this Directory by default, since isDefaultAccountStore and isDefaultGroupStore are both set to true.

Mapping a new Account Store

We would now like to map a new Account Store that will have the following characteristics:

  1. It will have the highest login priority. This means that it will be consulted first during the login process, before any other Account Stores.
  2. It will be the default Account Store for any new Accounts.
  3. It will be the default Group Store for any new Groups.

To accomplish this, you will send a POST:

POST v1/accountStoreMappings HTTP/1.1
Host: api.stormpath.com
Authorization: Basic MlpG...
Content-Type: application/json;charset=UTF-8

{
  "listIndex": 0,
  "isDefaultAccountStore": true,
  "isDefaultGroupStore": true,
  "application": {
    "href": "https://api.stormpath.com/v1/applications/1gk4Dxzi6o4PbdleXaMPLE"
  },
  "accountStore": {
    "href": "https://api.stormpath.com/v1/directories/2SKhstu8PlaekcaEXampLE"
  }
}

You are mapping the Application (id: 1gk4Dxzi6o4PbdleXaMPLE) to a new Directory (id: 2SKhstu8PlaekcaEXampLE). Additionally, you are setting

  1. the login priority to the highest priority, by sending a listIndex of 0.
  2. isDefaultAccountStore to true and
  3. isDefaultGroupStore to true as well.

So by sending a POST with these contents, you are able to create a new Account Store Mapping that supersedes the old one.

If you go back to the example from the Account Management chapter, you can see the Account Store Mapping between the Directory and the Application. This now means that the Captain’s Account in the Directory will now be able to log in to the Application.

<ERD with accountStoreMapping>

Updating an Existing Account Store

Updating an existing Account Store simply involves sending a request with the attributes that you would like to update.

Changing Login Priority

For example, if you want to update an existing Account Store to now have highest login priority, send this request that sets the Mapping’s list index value as 0:

POST /v1/accountStoreMappings/1NUhrCPT0q66bjyexample HTTP/1.1
Host: api.stormpath.com
Authorization: Basic MlpG...
Content-Type: application/json

{
  "listIndex": 0
}

The accountStoreMapping resource will be updated and all of the other Account Stores will have their listIndex incremented up by 1.

Changing the Default Account or Group Store

Setting an Account Store Mapping as the default Account or Group store would automatically supersede any other Account Store Mapping. Any other mapping that had previously been the default would have the “true” flag switched to “false”.

POST /v1/accountStoreMappings/1NUhrCPT0q66bjyexample HTTP/1.1
Host: api.stormpath.com
Authorization: Basic MlpG...
Content-Type: application/json

{
    "isDefaultAccountStore": "true",
    "isDefaultGroupStore": "true"
}

Note

Setting an Account Store Mapping’s Default Group/Account Store flag to false will not automatically set another Default Group/Account Store flag true. You are responsible for setting this yourself if you would like your Application to create new Accounts/Groups.

4.2. How Token-Based Authentication Works

In this section, you will discuss how to use Stormpath to generate and manage OAuth 2.0 Access Token.

4.2.1. Introduction to Token-Based Authentication

Since HTTP is considered a stateless protocol, if your application authenticates a user for one HTTP request, a problem arises when the next request is sent and your application doesn’t know who the user is. This is why many applications today pass some information to tie the request to a user. Traditionally, this requires Server-based authentication, where state is stored on the server and only a session identifier is stored on the client.

Token-based authentication is an alternate, stateless strategy. With token-based authentication, you secure an application based on a security token that is generated for the user on authentication and then stored on the client-side. Token-based Authentication is all about removing the need to store information on the server while giving extra security to keep the token secure on the client. This helps you as a developer build stateless and scalable applications.

Stormpath’s approach to token-based authentication has two elements: JSON Web Tokens (JWTs) for authentication, and OAuth 2.0 for authorization.

Why OAuth 2.0?

OAuth 2.0 is an authorization framework and provides a protocol to interact with a service that can delegate authentication or provide authorization. Its primary advantage as a standard is its wide adoption rate across many mobile and web applications today. If you have ever logged-in to a website using Facebook or Google, you have used one of OAuth 2.0’s many authorization flows. You can read more about the different OAuth 2.0 authorization flows or grant types in depth on Stormpath’s blog.

Even though OAuth 2.0 has many authorization modes or “grant types”, Stormpath currently supports the following:

  • Password Grant Type: Provides the ability to get an Access Token based on a login and password.
  • Client Credentials Grant Type: Provides the ability to exchange an API Key for an Access Token.
  • Social Grant Type: Allows you to exchange a user’s social Access Token or Authorization Code
  • Refresh Grant Type: Provides the ability to generate another Access Token based on a special Refresh Token.
  • Stormpath Factor Challenge Grant Type: Provides the ability to generate an Access Token with a Multi-Factor Authentication challenge code

To understand how to use Token-based Authentication, you need to talk about the different types of tokens that are available. To see how to generate an OAuth token, see below.

What Tokens Are Available for Token-Based Authentication?

For Token Based Authentication, there are a two different types of tokens that need to be managed. These are:

  • Access Token
  • Refresh Token

The Access Token is what grants access to a protected resource. The Access Token that Stormpath generates for Accounts on authentication is a JSON Web Token, or JWT. The JWT has security built-in to make sure that the Access Token is not tampered with on the client, and is only valid for a specified duration.

The Refresh Token is a special token that is used to generate additional Access Tokens. This allows you to have an short-lived Access Token without having to collect credentials every single time you need a new Access Token.

When using OAuth 2.0, the Access Token and Refresh Token are returned in the same response during the token exchange, this is called an Access Token Response.

4.2.2. Using Stormpath for Token-Based Authentication

Stormpath can be used to generate, manage, check, and revoke both Access and Refresh Tokens. Before diving in, let’s talk about configuration.

Configuring Token-Based Authentication

Stormpath is configurable so you can set the time to live (TTL) for both the Access and Refresh tokens. This is important for many applications because it gives the ability to define how the tokens expire. For example, you could decide that your application requires a user to log in daily, but the access should only live for 10 minutes. Or, you could decide that for your application, users should be able to stay logged-in for two months and the access token expires in an hour.

Each Application resource in Stormpath has an associated OAuth Policy resource where the TTLs for a particular Application’s tokens are stored:

{
    "href": "https://api.stormpath.com/v1/oAuthPolicies/1gk4Dxzi6o4PbdleXaMPLE",
    "accessTokenTtl": "PT1H",
    "refreshTokenTtl": "P60D",
    "comment":" // This JSON has been truncated for readability"
}

The values for both properties are stored as ISO 8601 Durations. By default, the TTL for the Access Token is 1 hour and the Refresh Token’s is 60 days. The maximum value for both is 10 years and 1 day (P10Y), while the minimum value is 1 second (PT1S).

If you wanted to change the TTL for the Access Token to 30 minutes and the Refresh Token to 7 days, you could send the following request:

POST /v1/oAuthPolicies/1gk4Dxzi6o4PbdleXaMPLE HTTP/1.1
Host: api.stormpath.com
Authorization: Basic MlpG...
Content-Type: application/json;charset=UTF-8

{
  "accessTokenTtl": "PT30M",
  "refreshTokenTtl": "P7D"
}

And you would get the following response:

HTTP/1.1 200 OK
Location: https://api.stormpath.com/v1/oAuthPolicies/1gk4Dxzi6o4PbdleXaMPLE
Content-Type: application/json;charset=UTF-8

{
  "href": "https://api.stormpath.com/v1/oAuthPolicies/1gk4Dxzi6o4PbdleXaMPLE",
  "accessTokenTtl": "PT30M",
  "refreshTokenTtl": "P7D",
  "comment":" // This JSON has been truncated for readability"
}

Note

Refresh Tokens are optional. If you would like to disable the Refresh Token from being generated, set a duration value of 0 (e.g. PT0M).

Generating an OAuth 2.0 Access Token

Stormpath can generate a brand new Access Token using the above-mentioned OAuth 2.0 grant types. This means that you can generate a new Access Token with:

  • Client Credentials Grant Type: a client’s credentials (e.g. Client ID and Secret)
  • Social Grant Type: a user’s social login Access Token or Authorization Code
  • Password Grant Type: a user’s credentials (e.g. username and password)
  • Stormpath Factor Challenge Type: a Stormpath Challenge href and code
  • Refresh Grant Type: For information about using the an OAuth Refresh token see below

The recommended way of generating an OAuth token now is to using the Client API. For more information on this, please see the Client API Guide’s Authentication chapter.

Note

The older, alternate way of generating an OAuth 2.0 token is documented here.

Validating an Access Token

Once an Access Token has been generated, you have taken care of the Authentication part of your workflow. Now, the OAuth token can be used to authorize individual requests that the user makes. To do this, the client will need to pass it to your application.

For example, if you have a route https://yourapplication.com/secure-resource, the client would request authorization to access the resource by passing the access token as follows:

GET /secure-resource HTTP/1.1
Host: https://yourapplication.com
Authorization: Bearer eyJraWQiOiIyWkZNV[...]dkhEZ2Z0THJ4Slp3dFExc2hFaTl2In0.xlCXL7UUVnMoBKj0p0bXM_cnraWo5Io-TvUt2WBOl3k

Once your application receives the request, the first thing to do is to validate the token, either using Stormpath, or using local application-side logic. The benefit of using Stormpath to validate the token through the REST API (or an SDK that is using the REST API) is that Stormpath can validate the token against the state of your Application and Account resources. To illustrate the difference:

Validation Criteria Locally Stormpath
Token hasn’t been tampered with Yes Yes
Token hasn’t expired Yes Yes
Token hasn’t been revoked No Yes
Account hasn’t been disabled or deleted No Yes
Issuer is Stormpath Yes Yes
Issuing Application is still enabled, and hasn’t been deleted No Yes
Account is still in an Account Store for the issuing Application No Yes

It is up to you to determine which kind of validation is important for your application. If you need to validate the state of the Account and/or Application resources, or if you need to use token revocation, then using Stormpath to validate the token is the obvious choice. If you only require that the token has not expired and has not been tampered with, you can validate the token locally and minimize the network requests to Stormpath.

Using Stormpath to Validate Tokens

To see how to validate tokens with Stormpath, let’s go back to the example where a user has already generated an access token.

To recap, you have done the following:

  1. Sent a POST to https://api.stormpath.com/v1/applications/$YOUR_APPLICATION_ID/oauth/token with a body that included information about the OAuth Grant Type you wanted, as well as your user’s username and password.
  2. Received back an Access Token Response, which contained - among other things - an Access Token in JWT format.

The user now attempts to access a secured resource by passing the access_token JWT value from the Access Token Response in the Authorization header:

GET /secure-resource HTTP/1.1
Host: https://yourapplication.com
Authorization: Bearer eyJraWQiOiIyWkZNVjRXV[...]

The Authorization header contains the Access Token. To validate this Token with Stormpath, you can issue an HTTP GET to your Stormpath Application’s /authTokens/ endpoint with the JWT token:

https://api.stormpath.com/v1/applications/$YOUR_APPLICATION_ID/authTokens/eyJraWQiOiIyWkZNVjRXV[...]

If the access token can be validated, Stormpath will return a 302 to the Access Token resource:

HTTP/1.1 302 Location Found
Location: https://api.stormpath.com/v1/accessTokens/6zVrviSEIf26ggXdJG097f

With the confirmation that the token is valid, you can now allow the user to access the secured resource that they requested.

Validating the Token Locally

Local validation would also begin at the point of the request to a secure resource:

GET /secure-resource HTTP/1.1
Host: https://yourapplication.com
Authorization: Bearer eyJraWQiOiIyWkZNVjRXV[...]

The token specified in the Authorization header has been digitally signed with the Stormpath API Key Secret that was used to generate the token.

This means that you can use a JWT library for your specific language to validate the token locally if necessary. For more information, please see one of our Integration Guides.

Refreshing Access Tokens

In the event that the Access Token expires, the user can generate a new one using the Refresh Token without re-entering their credentials.

To use this Refresh Token, you make an HTTP POST to your Applications /oauth/token endpoint with it and you will get a new token back.

POST /v1/applications/$YOUR_APPLICATION_ID/oauth/token HTTP/1.1
Host: api.stormpath.com
Authorization: Basic MlpG...
Content-Type: application/x-www-form-urlencoded

grant_type=refresh_token&refresh_token=eyJraWQiOiIyWkZNVjRXVlZDVkczNVhBVElJOVQ5Nko3IiwiYWxnIjoiSFMyNTYifQ.eyJqdGkiOiIxdkhEZ2Z0THJ4Slp3dFExc2hFaTl2IiwiaWF0IjoxNDQxMTE4Nzk2LCJpc3MiOiJodHRwczovL2FwaS5zdG9ybXBhdGguY29tL3YxL2FwcGxpY2F0aW9ucy8xZ2s0RHh6aTZvNFBiZGxCVmE2dGZSIiwic3ViIjoiaHR0cHM6Ly9hcGkuc3Rvcm1wYXRoLmNvbS92MS9hY2NvdW50cy8zYXBlbll2TDBaOXY5c3BkenBGZmV5IiwiZXhwIjoxNDQxNzIzNTk2fQ.xUjcxTZhWx74aa6adnUXjuvUgqjC8TvvrB7cBEmNF_g

This would be the response:

HTTP/1.1 200 OK
Content-Type: application/x-www-form-urlencoded

{
  "access_token": "eyJraWQiOiIyWkZNVjRXVlZDVkczNVhBVElJOVQ5Nko3IiwiYWxnIjoiSFMyNTYifQ.eyJqdGkiOiI2TnJXSXM1aWttSVBWSkNuMnA0bnJyIiwiaWF0IjoxNDQxMTMzNjQ1LCJpc3MiOiJodHRwczovL2FwaS5zdG9ybXBhdGguY29tL3YxL2FwcGxpY2F0aW9ucy8xZ2s0RHh6aTZvNFBiZGxCVmE2dGZSIiwic3ViIjoiaHR0cHM6Ly9hcGkuc3Rvcm1wYXRoLmNvbS92MS9hY2NvdW50cy8zYXBlbll2TDBaOXY5c3BkenBGZmV5IiwiZXhwIjoxNDQxMTM1NDQ1LCJydGkiOiIxdkhEZ2Z0THJ4Slp3dFExc2hFaTl2In0.SbSmuPz0-v4J2BO9-lpyz_2_T62mSB1ql_0IMrftpgg",
  "refresh_token": "eyJraWQiOiIyWkZNVjRXVlZDVkczNVhBVElJOVQ5Nko3IiwiYWxnIjoiSFMyNTYifQ.eyJqdGkiOiIxdkhEZ2Z0THJ4Slp3dFExc2hFaTl2IiwiaWF0IjoxNDQxMTE4Nzk2LCJpc3MiOiJodHRwczovL2FwaS5zdG9ybXBhdGguY29tL3YxL2FwcGxpY2F0aW9ucy8xZ2s0RHh6aTZvNFBiZGxCVmE2dGZSIiwic3ViIjoiaHR0cHM6Ly9hcGkuc3Rvcm1wYXRoLmNvbS92MS9hY2NvdW50cy8zYXBlbll2TDBaOXY5c3BkenBGZmV5IiwiZXhwIjoxNDQxNzIzNTk2fQ.xUjcxTZhWx74aa6adnUXjuvUgqjC8TvvrB7cBEmNF_g",
  "token_type": "Bearer",
  "expires_in": 1800,
  "stormpath_access_token_href": "https://api.stormpath.com/v1/accessTokens/6NrWIs5ikmIPVJCn2p4nrr"
}

Note that this response contains the same Refresh Token as was in the request. This is because when Stormpath generates a new Access Token for a Refresh Token it does not generate a new Refresh token, nor does it modify its expiration time. This means that once the Refresh Token expires, the user must authenticate again to get a new Access and Refresh Tokens.

Revoking Access and Refresh Tokens

There are cases where you might want to revoke the Access and Refresh Tokens that you have generated for a user. For example:

  • The user has explicitly logged out, and your application needs to revoke their access, requiring re-authentication.
  • The application, device, and/or client has been compromised and you need to revoke tokens for an Account.

To revoke the tokens, all you have to do is delete the Account’s /accessTokens/:accessTokenId resource.

First, you retrieve an Account’s Access and Refresh tokens. To do this, make an HTTP GET call for the Account information, then you will find the tokens inside the /accessTokens and /refreshTokens collections:

{
  "href": "https://api.stormpath.com/v1/accounts/3apenYvL0Z9v9spdzpFfey",
  "username": "jlpicard",
  "comment":" // This JSON has been truncated for readability",
  "accessTokens": {
    "href": "https://api.stormpath.com/v1/accounts/3apenYvL0Z9v9spdzpFfey/accessTokens"
  },
  "refreshTokens": {
    "href": "https://api.stormpath.com/v1/accounts/3apenYvL0Z9v9spexample/refreshTokens"
  }
}

If you then perform a GET on the accessTokens link, you will get back the individual tokens:

{
  "href": "https://api.stormpath.com/v1/accounts/3apenYvL0Z9v9spexample/accessTokens",
  "offset": 0,
  "limit": 25,
  "size": 1,
  "items": [
    {
      "href": "https://api.stormpath.com/v1/accessTokens/6NrWIs5ikmIPVJCexample",
      "comment":" // This JSON has been truncated for readability"
    }
  ]
}

Note

You can query the Access Tokens that an Account has for a specific Application by specifying the Application’s href as a URL parameter:

curl --request GET \
--user $SP_API_KEY_ID:$SP_API_KEY_SECRET \
--header 'content-type: application/json' \
--url "https://api.stormpath.com/v1/accounts/3apenYvL0Z9v9spexample//accessTokens?application.href=https://api.stormpath.com/v1/applications/1p4R1r9UBMQz0e5EXAMPLE"

To revoke the token, send the following request:

DELETE /v1/accessTokens/6NrWIs5ikmIPVJCexample HTTP/1.1
Host: api.stormpath.com
Authorization: Basic MlpG...

You will get back a 204 No Content response back from Stormpath when the call succeeds.

4.3. How Social Authentication Works

Social authentication essentially means using the “Log in with x” button in your application, where “x” is a Social Login Provider of some kind. There are a number of Social Login Providers that Stormpath supports out-of-the-box:

However, Stormpath also supports the creation of Social Directories for so-called Generic OAuth Providers. These Directories should work with any Social Login Provider that provides an OAuth 2.0 flow. For more information about this, see below.

Social Directories are a kind of mirrored Directory, in that they are used to mirror user information found in an external database. This means that entities like Groups can only exist in a your Stormpath Social Directory if they are mirrored in from the external Social provider. For more information, please see the Account Management chapter.

The Social Login Process

In general, the social login process works as follows:

  1. The user who wishes to authenticate will click a “Log in with x” link.
  2. The user authenticates and is asked by the Provider to accept the permissions required by your app.
  3. Once the user accepts the permissions, the Provider sends back the user’s information
  4. Stormpath will look for an Account in your application’s Directories that matches this information.
  1. If a matching Account is found, Stormpath will return the existing Account’s href.
  2. If a matching Account is not found, Stormpath will create one and return the new Account’s href.
  1. At this point, a language/framework-specific integration would use this href to create a Session for the user.

As a developer, integrating Social Login into your application with Stormpath only requires three steps:

  1. Create a Social Directory for your Provider.
  2. Map the Directory as an Account Store to an Application resource. When an Account Store (in this case a Directory) is mapped to an Application, the Accounts in the AccountStore are considered the Application’s users and they can log in to it.
  3. Include the appropriate social login button for that Provider, linking it to the Stormpath Client API’s /authorize endpoint. For more information about this, see below, or go to the Client API Guide.

Attribute Mappings

Each social provider returns their own specific information about a user. The returned information is stored inside that Account’s Provider Data in a userInfo object. This information can also be mapped to Stormpath Account attributes using mapping configured in the Directory Provider’s userInfoMappingRules.

For example, if we look at a Google user’s userInfo:

{
  "href":"https://api.stormpath.com/v1/accounts/1Voi7LQ6NGnTPskjn9eZBA/providerData",
  "createdAt":"2016-04-29T17:31:23.676Z",
  "modifiedAt":"2016-11-10T18:22:46.749Z",
  "accessToken":"ya2[...]Nhs",
  "providerId":"google",
  "refreshToken":null,
  "userInfo":{
    "id":"123111111111111111234",
    "email":"jakub@stormpath.com",
    "name":"Jakub Swiatczak",
    "firstName":"Jakub",
    "lastName":"Swiatczak",
    "link":"https://plus.google.com/1023540111341293534",
    "profilePictureUrl":"https://lh4.googleusercontent.com/-8HcexamplekMuE/AAAAAAAAAAI/AAAAAAAAACI/VtAVULicGV4/photo.jpg",
    "gender":"male",
    "locale":null
  }
}

We can see that Google returns an attribute called “gender”. We can map this to a piece of Custom Data attached to the Account by creating a new rule in the Directory Provider’s userInfoMappingRules.

POST /v1/userInfoMappingRules/3HuqEWMWi7wts1CEXAMPLE HTTP/1.1
Host: api.stormpath.com
Content-Type: application/json
Authorization: Basic MlpGT[...]xxdmk0

{
  "items":[
    {
      "name":"gender",
      "accountAttributes":[
        "customData.gender"
        ]
    }
    ]
}

The response is a 200 OK along with the User Info Mapping Rules:

{
  "href":"https://api.stormpath.com/v1/userInfoMappingRules/3HuqEWMWi7wts1CEXAMPLE",
  "createdAt":"2016-10-27T16:56:10.429Z",
  "modifiedAt":"2016-12-02T15:31:38.928Z",
  "items":[
    {
      "name":"gender",
      "accountAttributes":[
        "customData.gender"
      ]
    }
  ]
}

Any Accounts that log into this Directory will now have a new Custom Data value created and Google’s “gender” value will be placed in there.

Scopes

The Directory’s Provider now has a scope array where you can define what Scopes are requested from the Social provider. These arrays have default values that are automatically included for each different kind of provider.

Social Provider Default Scopes More Info
Google profile, email Google OAuth Scopes
Facebook public_profile, email Permissions With Facebook Login
GitHub user:email GitHub OAuth Scopes
LinkedIn r_basicprofile, r_emailaddress LinkedIn Profile Fields

Additional scopes can be selected by going to the Directory’s page in the Stormpath Admin Console and clicking on the Scopes tab found under “Provider Configuration” or by passing a scopes array to the Directory’s Provider resource:

POST /v1/directories/4LL10Q3FNkiMLWmXfFQ1OU/provider HTTP/1.1
Authorization: Basic NVdYR...
Content-Type: application/json; charset=utf-8
Host: api.stormpath.com

{
  "scopes":[
    "user_about_me"
  ]
}

Social Login Providers

4.3.1. Google

Before you integrate Google Login with Stormpath, you must complete the following steps:

  • Create an application in the Google Developer Console
  • Enable Google Login for your Google application
  • Retrieve the OAuth Credentials (Client ID and Secret) for your Google application
  • Add your application’s redirect URL, which is the URL the user will be returned to after successful authentication. If you are using the Client API, then this will be your Application’s /authorize endpoint (e.g. https://cold-diver.apps.stormpath.io/authorize/callback).

Note

Be sure to only enter the Redirect URL you are currently using. So, if you are running your app in development mode, set it to your local URL, and if you’re running your app in production mode, set it to your production URL.

For more information, please see the Google OAuth 2.0 documentation.

Step 1: Create a Social Directory for Google

Creating this Directory for Google requires that you provide information from Google as a Provider resource. This can be accomplished by creating a new Directory:

Note

Instead of specifying the Redirect URI in the Provider resource, you can also just include it as part of your authentication process. For more information, continue reading.

POST /v1/directories HTTP/1.1
Host: api.stormpath.com
Authorization: Basic MlpG...
Content-Type: application/json;charset=UTF-8

{
    "name" : "my-google-directory",
    "description" : "A Google directory",
    "provider": {
        "providerId": "google",
        "clientId":"YOUR_GOOGLE_CLIENT_ID",
        "clientSecret":"YOUR_GOOGLE_CLIENT_SECRET",
        "redirectUri":"YOUR_GOOGLE_REDIRECT_URI"
    }
}

Note

If you are using Google+ Sign-In for server-side apps, Google recommends that you leave the “Authorized Redirect URI” field blank in the Google Developer Console. In Stormpath, when creating the Google Directory, you must set the redirect URI to postmessage.

Step 2: Map the Google Directory as an Account Store for Your Application

Creating an Account Store Mapping between your new Google Directory and your Stormpath Application can be done as described in Mapping a new Account Store.

Step 3: Access an Account with Google Tokens

To access or create an Account in your new Google Directory you use the Client API’s /authorize endpoint. Full documentation of this endpoint can be found in the Client API Product Guide.

At a high level, the process works as follows:

  1. The user clicks on a “Login with Google” link pointing at your application’s /authorize endpoint
  2. Stormpath handles the login to Google and redirects the user back to your app with a Stormpath Token JWT response

You can then, for example, pass that Stormpath token to the /oauth/token endpoint and receive back OAuth 2.0 access and refresh tokens as described in Generating an OAuth 2.0 Access Token.

Note

The older, alternate way of handling this login is documented here.

4.3.2. Facebook

Before you integrate Facebook Login with Stormpath, you must complete the following steps:

  • Create an application on the Facebook Developer Site
  • Retrieve your OAuth credentials (App ID and App Secret)
  • Add your application’s private and public root URLs
  • Add your application’s redirect URL, which is the URL the user will be returned to after successful authentication. If you are using the Client API, then this will be your Application’s /authorize endpoint (e.g. https://cold-diver.apps.stormpath.io/authorize/callback).

For more information, please see the Facebook documentation.

Step 1: Create a Social Directory for Facebook

Creating this Directory requires that you provide information from Facebook as a Provider resource. This can be accomplished by creating a new Directory:

POST /v1/directories HTTP/1.1
Host: api.stormpath.com
Authorization: Basic MlpG...
Content-Type: application/json;charset=UTF-8

{
    "name" : "my-facebook-directory",
    "description" : "A Facebook directory",
    "provider": {
      "providerId": "facebook",
      "clientId":"YOUR_FACEBOOK_APP_ID",
      "clientSecret":"YOUR_FACEBOOK_APP_SECRET"
    }
}
Step 2: Map the Facebook Directory as an Account Store for Your Application

Creating an Account Store Mapping between your new Facebook Directory and your Stormpath Application can be done as described in Mapping a new Account Store.

Step 3: Access an Account with Facebook Tokens

To access or create an Account in your new Facebook Directory you use the Client API’s /authorize endpoint. Full documentation of this endpoint can be found in the Client API Product Guide.

At a high level, the process works as follows:

  1. The user clicks on a “Login with Facebook” link pointing at your application’s /authorize endpoint
  2. Stormpath handles the login to Facebook and redirects the user back to your app with a Stormpath Token JWT response

You can then, for example, pass that Stormpath token to the /oauth/token endpoint and receive back OAuth 2.0 access and refresh tokens as described in Generating an OAuth 2.0 Access Token.

Note

The older, alternate way of handling this login is documented here.

4.3.3. Github

Before you integrate GitHub Login with Stormpath, you must complete the following steps:

  • Create an application in the GitHub Developer Site
  • Retrieve OAuth Credentials (Client ID and Secret) for your GitHub application. If you are using the Client API, then this will be your Application’s /authorize endpoint (e.g. https://cold-diver.apps.stormpath.io/authorize/callback).
  • Add your application’s redirect URL, which is the URL the user will be returned to after successful authentication.

For more information, please see the GitHub documentation on registering your app.

Step 1: Create a Social Directory for GitHub

Creating this Directory requires that you provide information from GitHub as a Provider resource. This can be accomplished by creating a new Directory:

POST /v1/directories HTTP/1.1
Host: api.stormpath.com
Authorization: Basic MlpG...
Content-Type: application/json;charset=UTF-8

{
    "name" : "my-github-directory",
    "description" : "A GitHub directory",
    "provider": {
      "providerId": "github",
      "clientId":"YOUR_GITHUB_CLIENT_ID",
      "clientSecret":"YOUR_GITHUB_CLIENT_SECRET"
    }
}
Step 2: Map the GitHub Directory as an Account Store for Your Application

Creating an Account Store Mapping between your new GitHub Directory and your Stormpath Application can be done as described in Mapping a new Account Store.

Step 3: Access an Account with GitHub Tokens

To access or create an Account in your new GitHub Directory you use the Client API’s /authorize endpoint. Full documentation of this endpoint can be found in the Client API Product Guide.

At a high level, the process works as follows:

  1. The user clicks on a “Login with GitHub” link pointing at your application’s /authorize endpoint
  2. Stormpath handles the login to GitHub and redirects the user back to your app with a Stormpath Token JWT response

You can then, for example, pass that Stormpath token to the /oauth/token endpoint and receive back OAuth 2.0 access and refresh tokens as described in Generating an OAuth 2.0 Access Token.

Note

The older, alternate way of handling this login is documented here.

4.3.4 LinkedIn

Before you integrate LinkedIn Login with Stormpath, you must complete the following steps:

  • Create an application in the LinkedIn Developer Site
  • Add your application’s redirect URLs, which are the URL the user will be returned to after successful authentication. If you are using the Client API, then this will be your Application’s /authorize endpoint (e.g. https://cold-diver.apps.stormpath.io/authorize/callback).
  • Retrieve OAuth Credentials (Client ID and Secret) for your LinkedIn application

For more information, please see LinkedIn’s OAuth documentation.

Step 1: Create a Social Directory for LinkedIn

Creating this Directory requires that you provide information from LinkedIn as a Provider resource. This can be accomplished by creating a new Directory:

POST /v1/directories HTTP/1.1
Host: api.stormpath.com
Authorization: Basic MlpG...
Content-Type: application/json;charset=UTF-8

{
    "name" : "my-linkedin-directory",
    "description" : "A LinkedIn Directory",
    "provider": {
      "providerId": "linkedin",
      "clientId":"YOUR_LINKEDIN_APP_ID",
      "clientSecret":"YOUR_LINKEDIN_APP_SECRET"
    }
}
Step 2: Map the LinkedIn Directory as an Account Store for Your Application

Creating an Account Store Mapping between your new LinkedIn Directory and your Stormpath Application can be done as described in Mapping a new Account Store.

Step 3: Access an Account with LinkedIn Tokens

To access or create an Account in your new LinkedIn Directory you use the Client API’s /authorize endpoint. Full documentation of this endpoint can be found in the Client API Product Guide.

At a high level, the process works as follows:

  1. The user clicks on a “Login with LinkedIn” link pointing at your application’s /authorize endpoint
  2. Stormpath handles the login to LinkedIn and redirects the user back to your app with a Stormpath Token JWT response

You can then, for example, pass that Stormpath token to the /oauth/token endpoint and receive back OAuth 2.0 access and refresh tokens as described in Generating an OAuth 2.0 Access Token.

Note

The older, alternate way of handling this login is documented here.

4.3.5. Twitter

Before you integrate Twitter Login with Stormpath, you must complete the following steps:

  • Create an application on the Twitter Application Management Site
  • Retrieve your OAuth credentials (App ID and App Secret)
  • Add your application’s “Callback URL”, which is the URL the user will be returned to after successful authentication. If you are using the Client API, then this will be your Application’s /authorize endpoint (e.g. https://cold-diver.apps.stormpath.io/authorize/callback).

For more information, please see the Twitter documentation.

Step 1: Create a Social Directory for Twitter

Creating this Directory requires that you provide information from Twitter as a Provider resource. This can be accomplished by creating a new Directory:

POST /v1/directories HTTP/1.1
Authorization: Basic NVdYRVp...
Content-Type: application/json; charset=utf-8
Host: api.stormpath.com

{
  "name":"Twitter",
  "description":"A Twitter directory",
  "provider":{
    "providerId":"twitter",
    "clientId":"OybhZelDAuncdBX3KVmIExample",
    "clientSecret":"d2hzXHLZsJs4FdDX0l5Queen8Ww21v9L2T1YrOJAE2lLfExample"
  }
}
Step 2: Map the Twitter Directory as an Account Store for Your Application

Creating an Account Store Mapping between your new Twitter Directory and your Stormpath Application can be done as described in Mapping a new Account Store.

Step 3: Access an Account with Twitter Tokens

To access or create an Account in your new Twitter Directory you use the Client API’s /authorize endpoint. Full documentation of this endpoint can be found in the Client API Product Guide.

At a high level, the process works as follows:

  1. The user clicks on a “Login with Twitter” link pointing at your application’s /authorize endpoint
  2. Stormpath handles the login to Twitter and redirects the user back to your app with a Stormpath Token JWT response

You can then, for example, pass that Stormpath token to the /oauth/token endpoint and receive back OAuth 2.0 access and refresh tokens as described in Generating an OAuth 2.0 Access Token.

4.3.6. Generic OAuth 2.0 Login

While Stormpath supports any provider that offers login via an OAuth 2.0 flow, the following providers have been tested and confirmed as working:

  • Amazon
  • Imgur
  • Instagram
  • StackExchange
  • Twitch

You can find example configurations for these providers below. The instructions below should allow you to integrate with any other OAuth provider not listed here. If you have any questions about integrating with an OAuth provider not listed here, feel free to contact us at support@stormpath.com and we will help as best as we can.

Before you integrate your OAuth provider with Stormpath, you must complete the following steps:

  • Create an application with that provider
  • Enable OAuth 2.0 login with that provider
  • Add your application’s redirect URL, which is the URL the user will be returned to after successful authentication. If you are using the Client API, then this will be your Application’s /authorize endpoint (e.g. https://endless-winter.apps.stormpath.io/authorize/callback).

You will also need to gather the following information from the provider:

  • Client ID & Secret: These are the OAuth credentials that Stormpath will need in order to communicate with the provider.
  • Authorization Endpoint: Used to retrieve the Authorization Code from the provider. This endpoint will often end with /authorize.
  • Token Endpoint: Accepts the Authorization Code you retrieve from the Authorization Endpoint and returns an Access Token. This endpoint will often end with /token.
  • Resource Endpoint: Accepts the Access Token you got from the Token Endpoint and returns the user data. This is usually the endpoint that is used to retrieve the user object.
  • Access Token Type: This is the format of the token that is passed by Stormpath to the provider in order to retrieve the user data. Most providers support all three formats: bearer, oauth_token, access_token. bearer means that the token is passed as part of a Bearer Authentication header, while oauth_token and access_token are both URL parameters. Most providers support all three, but this is not always true.
  • ID Field: This is the field that contains the user ID in the user data that is retrieved by Stormpath from the Resource Endpoint.

Example configurations can be found below. Information about actually initiating Social Login can be found in the Client API documentation.

Step 1: Create a Directory for your OAuth provider

Creating this Directory requires that you provide information about your Provider resource. This can be accomplished by creating a new Directory:

POST /v1/directories HTTP/1.1
Authorization: Basic NVdYR...
Content-Type: application/json; charset=utf-8
Host: api.stormpath.com

{
  "name":"Amazon",
  "description":"A generic OAuth directory for Amazon",
  "provider":{
    "providerId":"amazon",
    "clientId":"amzn1.application-oa2-client.99daa08example302d144a8ea",
    "clientSecret":"93342d47bbecbexample0655228fced82",
    "authorizationEndpoint":"https://www.amazon.com/ap/oa",
    "tokenEndpoint":"https://api.amazon.com/auth/o2/token",
    "resourceEndpoint":"https://api.amazon.com/user/profile",
    "idField":"user_id",
    "accessTokenType":"bearer",
    "scope":[
      "profile"
    ]
  }
}
Step 2: Map the Directory as an Account Store for Your Application

Creating an Account Store Mapping between your new Google Directory and your Stormpath Application can be done as described in Mapping a new Account Store.

Step 3: Access an Account with the Client API

To access or create an Account in your new Directory you use the Client API’s /authorize endpoint. Full documentation of this endpoint can be found in the Client API Product Guide.

At a high level, the process works as follows:

  1. The user clicks on a “Login with ______” link pointing at your application’s /authorize endpoint
  2. Stormpath handles the login and redirects the user back to your app with a Stormpath Token JWT response

You can then, for example, pass that Stormpath token to the /oauth/token endpoint and receive back OAuth 2.0 access and refresh tokens as described in the Client API Guide.

Example Configurations

This section contains examples of Providers configured for 5 common OAuth login providers, though we have removed irrelevant values (e.g. createdAt), which means that you can copy this JSON and simply fill-in your own values where necessary.

ID Field

ID Field must refer to the location of the user’s ID as found within the user object returned from the Resource Endpoint. If not specified, the default value for this field is simply id.

Some providers return the ID field nested inside other objects. In this case, you must indicate the nesting using a period. For example, Imgur returns the id within a data object. This means that the value of idField should be data.id.

Provider ID

The provider ID can contain any string value that you like, but your Application cannot have more than one Directory with any given providerId value.

Amazon
{
  "accessTokenType": "bearer",
  "authorizationEndpoint": "https://www.amazon.com/ap/oa",
  "clientId": "YOUR_CLIENT_ID",
  "clientSecret": "YOUR_CLIENT_SECRET",
  "idField": "user_id",
  "providerId": "amazon",
  "providerType": "oauth2",
  "resourceEndpoint": "https://api.amazon.com/user/profile",
  "scope": [
    "profile"
  ],
  "tokenEndpoint": "https://api.amazon.com/auth/o2/token"
}

Note

In order for the flow to succeed, Amazon requires that you specify a scope. This example only shows one of the possible scopes. For more information see the Amazon Documentation.

Imgur
{
  "accessTokenType": "bearer",
  "authorizationEndpoint": "https://api.imgur.com/oauth2/authorize",
  "clientId": "YOUR_CLIENT_ID",
  "clientSecret": "YOUR_CLIENT_SECRET",
  "idField": "data.id",
  "providerId": "imgur",
  "providerType": "oauth2",
  "resourceEndpoint": "https://api.imgur.com/3/account/me",
  "scope": [],
  "tokenEndpoint": "https://api.imgur.com/oauth2/token"
}
Instagram
{
  "accessTokenType": "access_token",
  "authorizationEndpoint": "https://api.instagram.com/oauth/authorize",
  "clientId": "YOUR_CLIENT_ID",
  "clientSecret": "YOUR_CLIENT_SECRET",
  "idField": "data.id",
  "providerId": "instagram",
  "providerType": "oauth2",
  "resourceEndpoint": "https://api.instagram.com/v1/users/self",
  "scope": [],
  "tokenEndpoint": "https://api.instagram.com/oauth/access_token"
}
StackExchange
{
  "accessTokenType": "access_token",
  "authorizationEndpoint": "https://stackexchange.com/oauth",
  "clientId": "YOUR_CLIENT_ID",
  "clientSecret": "YOUR_CLIENT_SECRET",
  "idField": "items[0].user_id",
  "providerId": "stackexchange",
  "providerType": "oauth2",
  "resourceEndpoint": "https://api.stackexchange.com/2.2/me?order=desc&sort=reputation&site=stackapps&key=YOUR_APP_KEY",
  "scope": [
    "no_expiry"
  ],
  "tokenEndpoint": "https://stackexchange.com/oauth/access_token"
}

Note

In order for the flow to succeed, you must configure Stormpath to request a scope. This example uses the no_expiry scope. For more information, see the StackExchange documentation.

Also the Resource Endpoint requires you to include a key that is found on your StackApp’s configuration page.

Twitch
{
  "accessTokenType": "oauth_token",
  "authorizationEndpoint": "https://api.twitch.tv/kraken/oauth2/authorize",
  "clientId": "YOUR_CLIENT_ID",
  "clientSecret": "YOUR_CLIENT_SECRET",
  "idField": "_id",
  "providerId": "twitch",
  "providerType": "oauth2",
  "resourceEndpoint": "https://api.twitch.tv/kraken/user",
  "scope": [],
  "tokenEndpoint": "https://api.twitch.tv/kraken/oauth2/token"
}

4.4. Authenticating Against an LDAP Directory

This section assumes that you are already familiar both with 4.1.2. How Login Attempts Work in Stormpath and the concept of Stormpath LDAP Directories. The instructions in this section apply to all LDAP user directories, including Active Directory, with two exceptions:

The LDAP Authentication Flow

There are two different authentication flows for LDAP directories: one for Active Directory, and another for all other directories that use the LDAP protocol.

LDAP

Non-AD LDAP Auth Sequence

Active Directory Authentication

AD LDAP Auth Sequence

Mirror Directories and LDAP

To recap: With LDAP integration, Stormpath is simply mirroring the canonical LDAP user directory. If this fulfills your requirements, then the story ends here. However, if you need to support other kinds of login (and therefore other kinds of Directories) it is recommended that you maintain a “master” Directory alongside your Mirror Directory. For more about this, see 3.8. Account Linking.

Setting Up Login With LDAP

The step-by-step process for setting-up LDAP login is as follows:

Step 1: Create an LDAP Directory

HTTP POST a new Directory resource to the /directories endpoint. This Directory will contain a Provider resource with providerId set to ldap or ad. This Provider resource will in turn contain an LDAP Agent object, which in turn also contains a few nested configuration resources.

Note

All of these must be passed at the same time:

directory
  └──provider
      └──agent
          └──config
              ├──accountConfig
              └──groupConfig

The full Directory object, with all of the required resources, will look like this:

POST /v1/directories HTTP/1.1
Host: api.stormpath.com
Authorization: Basic MlpG...
Content-Type: application/json;charset=UTF-8

{
  "name":"My LDAP Directory",
  "description":"An LDAP Directory created with the Stormpath API",
  "provider":{
    "providerId":"ldap",
    "agent":{
      "config":{
        "directoryHost":"ldap.local",
        "directoryPort":"636",
        "sslRequired":true,
        "agentUserDn":"tom@stormpath.com",
        "agentUserDnPassword":"StormpathRulez",
        "baseDn":"dc=example,dc=com",
        "pollInterval":60,
        "accountConfig":{
          "dnSuffix":"ou=employees",
          "objectClass":"person",
          "objectFilter":"(cn=finance)",
          "emailRdn":"email",
          "givenNameRdn":"givenName",
          "middleNameRdn":"middleName",
          "surnameRdn":"sn",
          "usernameRdn":"uid",
          "passwordRdn":"userPassword"
        },
        "groupConfig":{
          "dnSuffix":"ou=groups",
          "objectClass":"groupOfUniqueNames",
          "objectFilter":"(ou=*-group)",
          "nameRdn":"cn",
          "descriptionRdn":"description",
          "membersRdn":"uniqueMember"
        }
      }
    }
  }
}

For more information about all of these values, please see the Reference chapter’s Directory section.

Step 2: Install your LDAP Agent

Note

The LDAP Agent can be installed anywhere, as long as the location allows it access to your LDAP server.

Once the Directory, Provider and Agent are created, installing your Agent is done in three steps.

1. Download

Download your Agent by following the Download link on the Agent page in the Admin Console.

2. Configure

a. Make sure Java 1.8 is installed

b. Unzip to a location in your file system, for example C:\stormpath\agent in Windows or /opt/stormpath/agent in Unix.

In the same location, open the file dapper.properties from the config folder and replace this line:

agent.id = PutAgentSpecificIdHere

With this line:

agent.id  = 72MlbWz6C4dLo1oBhgjjTt

Follow the instructions in the dapper.properties file to reference your account’s API authentication.

3. Start

In Windows:

(Go to your agent directory, for example C:\stormpath\agent)

C:\stormpath\agent>cd bin
C:\stormpath\agent\bin>startup.bat

In Unix:

(Go to your agent directory, for example /opt/stormpath/agent)

$ cd bin
$ startup.sh

The Agent will start synchronizing immediately, pushing the configured data to Stormpath. You will see the synchronized user Accounts and Groups appear in the Stormpath Directory, and the Accounts will be able to log in to any Stormpath-enabled application that you assign. When the Agent detects local changes, additions or deletions to the mirrored Accounts or Groups, it will automatically propagate those changes to Stormpath.

Step 3: Map the LDAP Directory as an Account Store for Your Application

Creating an Account Store Mapping between your new LDAP Directory and your Stormpath Application can be done as described in Mapping a new Account Store.

The log-in process will now proceed as it would for any other kind of Directory.

Note

In the case of Active Directory, the login process does not proceed as normal, and instead involves something called “delegated authentication”. The user accounts pulled-in from an Active Directory do not include the passwords. Consequently, the LDAP Agent does double duty in the case of Active Directory: it synchronizes accounts, and also handles authentication attempts and relays the outcome back to Stormpath.

4.5. Authenticating Against a SAML Directory

SAML is an XML-based standard for exchanging authentication and authorization data between security domains. The security domains are the Service Provider and the Identity Provider. The Service Provider is the service that the user is trying to access, but access requires authentication. This authentication is done with the Identity Provider, which is the canonical source of user information.

Stormpath is able to function as either end of the SAML authentication. This means that your Stormpath-powered application could either be the Identity Provider, and provide authentication for a third-party application such as Zendesk, or it could be the Service Provider, and require authentication with a third-party Identity Provider like Okta.

If you’d like to understand the steps involved in a SAML login, see the SAML Login Flow section.

If you’d like to know more about using Stormpath as an Identity Provider, see 4.5.1. Stormpath as the Identity Provider.

If you’d like to know more about using Stormpath as a Service Provider, see 4.5.2. Stormpath as the Service Provider.

If you want a step-by-step guide to configuring Stormpath to work as a Service Provider with Identity Providers like Salesforce, OneLogin and Okta, as well as with your ADFS or Azure AD deployment, see the Stormpath Admin Console Guide.

Note

Configuring Stormpath as an Identity Provider is currently not supported in the Admin Console.

If you’d like to know about how to configure SAML using just the REST API, please see either:

4.5.1. Stormpath as the Identity Provider

In this case, Stormpath is serving as the IdP, which is the repository for user data. A request is first made to the Service Provider, who redirects to Stormpath. Authentication is done against Stormpath, and then the user is passed back to the Service Provider in an authenticated state.

For example:

  1. A user tries to access Zendesk: https://samlexample.zendesk.com/
  2. The user is redirected to your Stormpath ID Site page: https://iron-troop.id.stormpath.io/#/
  3. The user authenticates with Stormpath
  4. On successful authentication, the user is redirected back to Zendesk

The steps below will use Zendesk as an example, although any Service Provider that supports SAML should be compatible.

Note

Stormpath as an Identity Provider (IdP) supports only the Service Provider initiated flow.

Configuring Stormpath as an Identity Provider via REST

Here you will find the steps that are required to configure Stormpath as a SAML Identity Provider using only the REST API.

Note

Currently Stormpath as an Identity Provider can only be configured via REST.

Step 1: Gather IdP Data from Stormpath

The Stormpath IdP data can be found in your Application’s SAML Policy, inside the identityProvider object:

GET /v1/samlIdentityProviders/$SAML_IDP_ID HTTP/1.1
Host: api.stormpath.com
Authorization: Basic MlpG...
Content-Type: application/xml
{
  "href": "https://api.stormpath.com/v1/samlIdentityProviders/7l4yLIJYpzpdfj7gEvDiz7",
  "createdAt": "2017-01-31T23:28:39.595Z",
  "modifiedAt": "2017-01-31T23:28:39.595Z",
  "status": "ENABLED",
  "ssoLoginEndpoint": {
    "href": "https://wise-galaxy.apps.stormpath.io/saml/sso"
  },
  "signatureAlgorithm": "RSA-SHA256",
  "shaFingerprint": "412f20963b65114d7301350274b66b935c70e702",
  "x509SigningCert": {
    "href": "https://api.stormpath.com/v1/x509certificates/7l4YOyYSiX0Noaas18Oifd"
  },
  "metadata": {
    "href": "https://api.stormpath.com/v1/samlIdentityProviderMetadatas/7l4yLLddka8cIYDXRB4zbB"
  },
  "attributeStatementMappingRules": {
    "href": "https://api.stormpath.com/v1/attributeStatementMappingRules/7l4phBjC8AtDigHPUzc3Xx"
  },
  "registeredSamlServiceProviders": {
    "href": "https://api.stormpath.com/v1/samlIdentityProviders/7l4yLIJYpzpdfj7gEvDiz7/registeredSamlServiceProviders"
  },
  "samlServiceProviderRegistrations": {
    "href": "https://api.stormpath.com/v1/samlIdentityProviders/7l4yLIJYpzpdfj7gEvDiz7/samlServiceProviderRegistrations"
  }
}

Your Service Provider will require some of the information in this resource:

  • SSO Login Endpoint: The URL at the IdP to which SAML authentication requests should be sent.
  • Signature Algorithm: The algorithm used to encode the x.509 certificate.
  • SHA Fingerprint: A short identifier for the x.509 certificate
  • x509 Signing Certificate: This certificate is used to sign the requests sent by your Service Provider to Stormpath.

Some or all of this information needs to be entered into your Service Provider’s SAML configuration.

Step 2: Configure your Service Provider inside Stormpath

You will need the following information from your SAML Service Provider:

  • Assertion Consumer Service (ACS) URL: The Service Provider endpoint to which SAML responses are sent.
  • Entity ID: The unique identifier for the Service Provider.
  • Name ID Format: The unique identifier used by the Service Provider for its user entities.

Note

Be especially careful with these values. Even a minor change can cause an error. For example, adding a slash at the end of the Zendesk entityId like so: https://YourSubdomain.zendesk.com/ will result in an error. Removing the final slash will fix the error.

The Name ID Format can have three possible values in Stormpath:

  • EMAIL
  • PERSISTENT
  • TRANSIENT

Your Service Provider’s documentation likely has the Name ID Format as a URN, in which case the end of that URN will tell you which value to use.

For example, Zendesk’s SAML metadata specifies this: <NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress</NameIDFormat>, which means that for a Zendesk configuration in Stormpath you would give nameIdFormat as EMAIL.

This information is passed to Stormpath to create a new Registered SAML Service Provider:

POST /v1/registeredSamlServiceProviders HTTP/1.1
Authorization: Basic NVdY...
Content-Type: application/json; charset=utf-8
Host: api.stormpath.com

{
  "name":"Zendesk Example",
  "assertionConsumerServiceURL":"https://jakubsamltest.zendesk.com/access/saml",
  "entityId":"https://jakubsamltest.zendesk.com",
  "nameIdFormat":"EMAIL"
}

On success, you will get back the SAML Service Provider resource:

{
  "href": "https://api.stormpath.com/v1/registeredSamlServiceProviders/2wXFkagK2s52nJKFtCZoa4",
  "createdAt": "2017-02-01T22:32:56.523Z",
  "modifiedAt": "2017-02-01T22:32:56.523Z",
  "name": "Zendesk Example",
  "description": "A description",
  "assertionConsumerServiceUrl": "https://jakubsamltest.zendesk.com/access/saml",
  "nameIdFormat": "EMAIL",
  "entityId": "https://jakubsamltest.zendesk.com/",
  "encodedX509Certificate": null,
  "tenant": {
    "href": "https://api.stormpath.com/v1/tenants/1gBTncWsp2ObQGgDn9R91R"
  }
}
Step 3: Map the Service Provider to an Identity Provider

As the final step in your Stormpath configuration, you will need to map the Service Provider resource that you just created to an existing SAML Identity Provider. Specifically, you want to map it to the SAML Identity Provider resource that you retrieved in Step 1.

POST /v1/samlIdentityProviders/7l4yLIJYpzpdfj7gEvDiz7/samlServiceProviderRegistrations HTTP/1.1
Authorization: Basic NVdY...
Content-Type: application/json; charset=utf-8
Host: api.stormpath.com

{
  "serviceProvider": {
    "href": "https://api.stormpath.com/v1/registeredSamlServiceProviders/2wXFkagK2s52nJKFtCZoa4"
  }
}

On success you will get back the mapping object:

{
  "href": "https://api.stormpath.com/v1/samlServiceProviderRegistrations/uwpZw2A23ARPvxGSuBYUO",
  "createdAt": "2017-02-01T22:38:14.359Z",
  "modifiedAt": "2017-02-01T22:38:14.359Z",
  "status": "ENABLED",
  "defaultRelayState": null,
  "serviceProvider": {
    "href": "https://api.stormpath.com/v1/registeredSamlServiceProviders/2wXFkagK2s52nJKFtCZoa4"
  },
  "identityProvider": {
    "href": "https://api.stormpath.com/v1/samlIdentityProviders/7l4yLIJYpzpdfj7gEvDiz7"
  }
}

At this point, all you need to do is visit your Service Provider. You should be directed to the Stormpath ID Site, and upon logging-in, you will arrive back at your Service Provider in an authenticated state.

4.5.2. Stormpath as the Service Provider

Stormpath as a Service Provider supports both Service Provider (SP) initiated and Identity Provider (IdP) initiated SAML authentication. In SAML terminology, the user is the User Agent, your application (along with Stormpath) is the Service Provider, and the third-party SAML authentication site is the Identity Provider or IdP.

Note

When Stormpath is functioning as the Service Provider, SAML Directories are a kind of mirrored Directory, in that they are used to mirror user information found in an external database. This means that entities like Groups can only exist in a your Stormpath SAML Directory if they are mirrored in from the external SAML IdP. For more information, please see the Account Management chapter.

Identity Provider Initiated SAML Authentication

With IdP-initiated SAML Authentication, the user authenticates first with the Identity Provider, and then logs into your Stormpath-enabled application from a screen inside the IdP’s site.

The IdP initiated process looks like this:

  1. User Agent authenticates with the IdP
  2. User Agent requests login to Your Application
  3. IdP redirects the user to Your Application along with SAML assertions
  4. Service Provider receives SAML assertions and either creates or retrieves Account information

Service Provider Initiated SAML Authentication

In this scenario, a user requests a protected resource (e.g. your application). Your application, with the help of Stormpath, then confirms the user’s identity in order to determine whether they are able to access the resource.

The broad strokes of the process are as follows:

  1. User Agent requests access from Service Provider
  2. Service Provider responds with redirect to Identity Provider
  3. Identity Provider authenticates the user
  4. Identity provider redirects user back to Service Provider along with SAML assertions.
  5. Service Provider receives SAML assertions and either creates or retrieves Account information

In both cases, just like with Mirror and Directories, the user information that is returned from the IdP is used by Stormpath to either identify an existing Account resource, or create a new one. In the case of new Account creation, Stormpath will map the information in the response onto its own resources. In the following section you will walk you through the process of configuring your SAML Directory, as well as giving you an overview of how the SAML Authentication process works.

For a more detailed step-by-step account of SAML login, see below.

Configuring Stormpath as the Service Provider

SAML configuration instructions can be found in the SAML Appendix of the Stormpath Admin Console Guide.

Salesforce

Instructions for configuring Salesforce SAML can be found in the Stormpath Admin Console Guide.

OneLogin

Instructions for configuring OneLogin SAML can be found in the Stormpath Admin Console Guide.

Okta

Instructions for configuring Okta SAML can be found in the Stormpath Admin Console Guide.

Ping

Instructions for configuring Ping SAML can be found in the Stormpath Admin Console Guide.

Active Directory Federation Services

Instructions for configuring ADFS SAML can be found in the Stormpath Admin Console Guide.

Azure Active Directory

Instructions for configuring ADFS SAML can be found in the Stormpath Admin Console Guide.

Configuring Stormpath as a Service Provider via REST

Here we will explain to you the steps that are required to configure Stormpath as a SAML Service Provider using only the REST API.

It is recommend that you configure SAML using the Stormpath Admin console, as explained in the Client API documentation. However, understanding the REST underpinnings of those instructions will allow you to automate some or all of the configuration process, if that is something that your application requires.

Also, currently the IdP-initiated flow can only be configured via REST, and not yet via the Stormpath Admin Console.

SAML configuration data is stored in the Directory’s Provider resource as well as in the Application. Both of these resources must also be linked with an Account Store Mapping.

Note

The steps here are nearly identical regardless of whether you are configuring Service Provider initiated or IdP initiated authentication. The only difference is in Step 5a.

Step 1: Gather IDP Data

You will need the following information from your IdP:

  • SSO Login URL - The URL at the IdP to which SAML authentication requests should be sent. This is often called an “SSO URL”, “Login URL” or “Sign-in URL”.
  • SSO Logout URL - The URL at the IdP to which SAML logout requests should be sent. This is often called a “Logout URL”, “Global Logout URL” or “Single Logout URL”.
  • Signing Cert - The IdP will digitally sign auth assertions and Stormpath will need to validate the signature. This will usually be in .pem or .crt format, but Stormpath requires the text value.
  • Signing Algorithm - You will need the name of the signing algorithm that your IdP uses. It will be either “RSA-SHA256” or “RSA-SHA1”.
Step 2: Configure Your SAML Directory

Input the data you gathered in Step 1 above into your Directory’s Provider resource, and then pass that along as part of the Directory creation request:

POST /v1/directories HTTP/1.1
Host: api.stormpath.com
Authorization: Basic MlpG...
Content-Type: application/json;charset=UTF-8

{
  "name" : "My SAML Directory",
  "description" : "A Directory used for SAML Authorization",
  "provider": {
    "providerId":"saml",
    "ssoLoginUrl":"https://yourIdp.com/saml2/sso/login",
    "ssoLogoutUrl":"https://yourIdp.com/saml2/sso/logout",
    "encodedX509SigningCert":"-----BEGIN CERTIFICATE-----\n...Certificate goes here...\n-----END CERTIFICATE-----",
    "requestSignatureAlgorithm":"RSA-SHA256"
  }
}

Note

Notice that new lines in the certificate are separated with a \n character.

Step 3: Retrieve Your Service Provider Metadata

Next you will have to configure your Stormpath-powered application as a Service Provider in your Identity Provider. This means that you will need to retrieve the correct metadata from Stormpath.

In order to retrieve the required values, start by sending this request:

GET /v1/directories/$DIRECTORY_ID/provider HTTP/1.1
Host: api.stormpath.com
Authorization: Basic MlpG...
Content-Type: application/json

This will return the Provider:

{
  "href":"https://api.stormpath.com/v1/directories/1joyMCilyf1xSravQxaHxy/provider",
  "createdAt":"2015-12-21T20:27:16.190Z",
  "modifiedAt":"2015-12-21T20:27:16.190Z",
  "providerId":"saml",
  "ssoLoginUrl":"https://stormpathsaml-dev-ed.my.salesforce.com/idp/endpoint/HttpRedirect",
  "ssoLogoutUrl":"https://stormpathsaml-dev-ed.my.salesforce.com/idp/endpoint/HttpRedirect",
  "encodedX509SigningCert":"-----BEGIN CERTIFICATE-----\nexample\n-----END CERTIFICATE-----",
  "requestSignatureAlgorithm":"RSA-SHA256",
  "attributeStatementMappingRules":{
    "href":"https://api.stormpath.com/v1/attributeStatementMappingRules/1jq5X3PhdEZJ5EL5MORdTG"
  },
  "serviceProviderMetadata":{
    "href":"https://api.stormpath.com/v1/samlServiceProviderMetadatas/1l4aLK8aJPNtwslBgXBjGE"
  }
}

Now you will need to retrieve your Directory Provider’s Service Provider Metadata:

GET /v1/samlServiceProviderMetadatas/$METADATA_ID HTTP/1.1
Host: api.stormpath.com
Authorization: Basic MlpG...
Content-Type: application/xml

Note

This will return XML by default, but you can also specify application/json if you’d like to receive JSON instead.

Example XML

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata" entityID="urn:stormpath:directory:5rHYCSu9IjzKz5pkyId5eR:provider:sp">
    <md:SPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
        <md:KeyDescriptor use="signing">
            <ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
                <ds:X509Data>
                    <ds:X509Certificate>MIIC2DCCAcCgAwIBAgIRAMExAMPLE</ds:X509Certificate>
                </ds:X509Data>
            </ds:KeyInfo>
        </md:KeyDescriptor>
        <md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress</md:NameIDFormat>
        <md:AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="http://api.stormpath.com/v1/directories/5rHYCSu9IjzKz5pEXample/saml/sso/post" index="0"/>
    </md:SPSSODescriptor>
</md:EntityDescriptor>

Example JSON

{
  "href":"http://api.stormpath.com/v1/samlServiceProviderMetadatas/173pHdbJ96DpPeuExaMPLE",
  "createdAt":"2015-12-09T19:22:10.033Z",
  "modifiedAt":"2015-12-09T19:22:10.033Z",
  "entityId":"urn:stormpath:directory:15iM83Y77qIIviKlTzGqjX:provider:sp",
  "assertionConsumerServicePostEndpoint":{
    "href":"http://api.stormpath.com/v1/directories/5rHYCSu9IjzKz5pEXample/saml/sso/post"
  },
  "x509SigningCert":{
    "href":"http://api.stormpath.com/v1/x509certificates/1712LVrz0fNSMk2y20EzfL"
  }
}

From this metadata, you will need two values:

  • Assertion Consumer Service Endpoint: This is the location the IdP will send its response to.
  • X509 Signing Certificate: The certificate that is used to sign the requests sent to the IdP. If you retrieve XML, the certificate will be embedded. If you retrieve JSON, you’ll have to follow a further /x509certificates link to retrieve it.

You will also need two other values, which will always be the same:

  • SAML Request Binding: Set to HTTP-Redirect.
  • SAML Response Binding: Set to HTTP-Post.
Step 4: Configure Your Service Provider in Your Identity Provider

Log-in to your Identity Provider (Salesforce, OneLogin, etc) and enter the information you retrieved in the previous step into the relevant application configuration fields. The specific steps to follow here will depend entirely on what Identity Provider you use, and for more information you should consult your Identity Provider’s SAML documentation.

Step 5: Configure Your Application

Your Stormpath Application Resource has two parts that are relevant to SAML:

  1. An authorizedCallbackUri Array that defines the authorized URIs that the IdP can return your user to with the authentication result. These should be URIs that you host yourself.

You should create any URIs here that you would like included as authorized callback URIs, to a maximum of 4000 characters in total length.

POST /v1/applications/$APPLICATION_ID HTTP/1.1
Host: api.stormpath.com
Authorization: Basic MlpG...
Content-Type: application/json;charset=UTF-8

{
  "authorizedCallbackUris": [
    "https://myapplication.com/whatever/callback",
    "https://myapplication.com/whatever/callback2"
  ]
}
  1. There is also an embedded samlPolicy object that contains information about the SAML flow configuration and endpoints:
{
  "href":"https://api.stormpath.com/v1/samlServiceProviders/61fOguTd49bCKEJbuLnFHO",
  "createdAt":"2016-01-18T21:02:24.501Z",
  "modifiedAt":"2016-01-18T21:02:24.501Z",
  "ssoInitiationEndpoint":{
    "href":"https://api.stormpath.com/v1/applications/61eykaiWwglwT5mngYyExu/saml/sso/idpRedirect"
  },
  "defaultRelayStates":{
    "href":"https://api.stormpath.com/v1/samlServiceProviders/61fOguTd49bCKEJbuLnFHO/defaultRelayStates"
  }
}
Step 5a: Generate defaultRelayState (IdP-initiated Authentication Only)

To configure your IdP for IdP-initiated authentication, you will need to get a defaultRelayState JWT:

POST /v1/samlServiceProviders/6voAya1BvrNeFOAeXamPle/defaultRelayStates HTTP/1.1
Host: api.stormpath.com
Authorization: Basic MlpG...

This request will return a response containing a JWT like this:

{
  "defaultRelayState": "eyJ0aWQiOiIxZ0JUbmNXc3AyT2JRR2dEbjlSOTFSIiwiYWxnIjoiSFMyNTYifQ.eyJzcFVpZCI6IjZ2b0F5YTFCdnJOZUZPQW9neGJ4T2UiLCJqdGkiOiIxdjdjT1l1SE1kQzA0Z2Vucm1wU2lZIn0.WvfWRxTfjRoPxA803HyOR380u2dWpdtQiO0I2kislFY"
}

This JWT will then need to be entered into your IdP’s configuration in order for IdP-initiated authentication to function properly.

This defaultRelayStates/ endpoint also accepts a few optional properties. These properties can be encoded in the defaultRelayState JWT that is stored on your IdP by passing them in the body of your POST:

  • callbackUri: Specifies the callBackUri to direct users to. Useful if there are multiple callbackUris specified in your Application.
  • organization: Allows you to specify an Organization to check users for.
  • state: Any state that your application would like to receive. Note that the application developer will need to interpret this state.

A request including these optional properties looks like this:

POST /v1/samlServiceProviders/6voAya1BvrNeFOAeXamPle/defaultRelayStates HTTP/1.1
Host: api.stormpath.com
Authorization: Basic MlpG...

{
    "callbackUri": "https://org1.myapp.com",
    "organization": {
        "nameKey": "org1",
    }
    "state": "IAmAState"
}
Step 6: Add the SAML Directory as an Account Store

Now the last thing you have to do is map the new Directory to your Application with an Account Store Mapping as described in Mapping a new Account Store.

Step 7: Configure SAML Attribute Mapping (Optional)

The Identity Provider’s SAML response contains assertions about the user’s identity, which Stormpath can use to create and populate a new Account resource.

<saml:AttributeStatement>
  <saml:Attribute Name="uid" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
    <saml:AttributeValue xsi:type="xs:string">test</saml:AttributeValue>
  </saml:Attribute>
  <saml:Attribute Name="mail" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
    <saml:AttributeValue xsi:type="xs:string">jane@example.com</saml:AttributeValue>
  </saml:Attribute>
    <saml:Attribute Name="location" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
    <saml:AttributeValue xsi:type="xs:string">Tampa, FL</saml:AttributeValue>
  </saml:Attribute>
</saml:AttributeStatement>

The Attribute Assertions (<saml:AttributeStatement>) are brought into Stormpath and become Account and customData attributes.

SAML Attribute mapping is defined in an attributeStatementMappingRules object found inside the Directory’s Provider object, or directly: /v1/attributeStatementMappingRules/$RULES_ID.

Mapping Rules

The rules have three different components:

  • name: The SAML Attribute name
  • nameFormat: The name format for this SAML Attribute, expressed as a Uniform Resource Name (URN).
  • accountAttributes: This is an array of Stormpath Account or customData (customData.$KEY_NAME) attributes that will map to this SAML Attribute.

Example Rule

{
  "name": "uid",
  "nameFormat": "urn:oasis:names:tc:SAML:2.0:attrname-format:basic",
  "accountAttributes":[
    "username"
  ]
}

The rule expressed here is as follows:

  • A SAML Assertion with the name uid AND
  • the name format urn:oasis:names:tc:SAML:2.0:attrname-format:basic
  • maps to the Account Attribute username.

Note

It is possible to specify only a name or nameFormat in your rule, instead of both.

In order to create the mapping rules, you send the following request:

POST /v1/attributeStatementMappingRules/$MAPPING_RULES_ID", HTTP/1.1
Host: api.stormpath.com
Authorization: Basic MlpG...
Content-Type: application/json;charset=UTF-8

{
  "items":[
    {
      "name":"uid",
      "accountAttributes":[
        "username"
      ]
    },
    {
      "name":"mail",
      "accountAttributes":[
        "email"
      ]
    },
    {
      "name":"location",
      "accountAttributes":[
        "customData.location"
      ]
    }
  ]
}

4.5.4. The Stormpath SAML Flow

There are three kinds of SAML flows that are possible:

  1. Service Provider-initiated, with Stormpath as the Identity Provider
  2. Service Provider-initiated, with Stormpath as the Service Provider
  3. Identity Provider-initiated, with Stormpath as the Service Provider

With Stormpath as the Identity Provider

The flow that has Stormpath as your Identity Provider is more simplified than the other two because it makes use of the new Client API endpoints.

IdP Initiated SAML Flow

Stormpath as the SAML IdP

Note

Depending on the Service Provider, the redirect may not happen automatically. The user may need to click on a “Login with x” link before they are redirected to the Stormpath Client API.

Since Stormpath handles the majority of these steps automatically, no further information is required to get this process working.

With Stormpath as the Service Provider

The two SAML authentication flows that Stormpath supports differ primarily in their starting points, and so the Service Provider (SP) initiated flow is really just the Identity Provider (IdP) initiated flow with a different starting point.

The Identity Provider Initiated Flow

In the Identity Provider Initiated flow, the user starts at the Identity Provider (IdP). After logging-in to the IdP, the user selects the Stormpath-enabled web application from within the IdP’s site, and is redirected to the application in an authenticated state.

IdP Initiated SAML Flow

The IdP Initiated SAML Flow

Step 1: Identity Provider Login

First the user will have to authenticate with the Identity Provider. They will then be provided with a list of configured applications that they are able to log in to. If they choose to log in to your Stormpath-enabled application, this will result in the IdP redirecting them to Stormpath.

Step 2: Redirect to Assertion Consumer Service URL

The user is redirected to the Assertion Consumer Service URL (/saml/sso/post) that is found in the Service Provider Metadata. At this point, an Account will either be retrieved (if it already exists) or created (if it doesn’t exist already).

Note

Account matching is done on the basis of the returned email address.

Step 3: Stormpath Response with JWT

The user will now be redirected by Stormpath back to your Application along with a JSON Web Token.

HTTP/1.1 302 Redirect
Location: https://myapplication.com/whatever/callback?jwtResponse=$RESPONSE_JWT
SAML Account Assertion JWT

This JWT again contains both Headers and a Body with Claims.

Header

Claim Name Required? Valid Value(s)
typ Yes The type of token, which will be JWT
alg Yes The algorithm that was used to sign this key. The only possible value is HS256.
stt Yes The Stormpath Token Type. In this case it will always be assertion.
kid Yes The ID of the Stormpath API Key that signed this JWT.

Body

Claim Name Description
iss The issuer of this token, which will contain your Application href.
sub The subject of the JWT. This will be an href for the Stormpath Account that signed up or logged into the SAML IdP. This href can be queried by using the REST API to get more information about the Account.
aud The audience of the JWT. This will match your API Key ID from Stormpath.
exp The expiration time for the JWT in Unix time.
iat The time at which the JWT was created, in Unix time.
jti A one-time-use-token for the JWT. If you require additional security around the validation of the token, you can store the jti in your application to validate that a particular JWT has only been used once.
state The state of your application, if you have chosen to have this passed back.
status For a SAML IdP the only possible status is AUTHENTICATED.
irt The UUID of the SAML Assertion response. This could be cached as a nonce in order to prevent replay attacks.
isNewSub Indicates whether this is a new Account in Stormpath.

At this point your user is authenticated and able to use your app.

The Service Provider Initiated Flow

In the Service Provider Initiated flow, the user starts at a login page (either ID Site or one inside a Stormpath-powered application), then is redirected to the Identity Provider. After authenticating with the Identity Provider, the user is returned to the application in an authenticate state.

Service Provider Initiated SAML Flow

The Service Provider Initiated SAML Flow

Step 1: Generate a JWT

The user agent will request to login with SAML. You will need to generate a JWT using an approved JWT library.

Below are language specific JWT libraries that Stormpath has sanity tested with ID Site.

SAML Authentication JWT

Note

This key must be signed with your API Key Secret.

The token itself will contain two parts, a Header and a Body that itself contains claims.

Header

Header Name Required? Valid Value(s)
kid Yes Your Stormpath API Key ID.
alg Yes The algorithm that was used to sign this key. The only valid value is HS256.

Body

The claims for the JWT body are as follows:

Claim Name Required? Valid Value(s)
iat Yes The “Issued At Time”, which is the time the token was issued, expressed in Unix time.
iss Yes The issuer of the token. This is your Application href.
cb_uri No The callback URI to use once the user takes an action on the ID Site or Identity provider. This URI will be passed a secure JWT as a URL parameter, and it’s job will be to validate that JWT and return the Stormpath Account. This URI must match a Authorized Callback URI on an Application resource, otherwise the flow will default to the first Callback URI that does not contain a wildcard.
jti Yes A universally unique identifier for the token. This can be generated using a GUID or UUID function of your choice.
state No The state of the application that you need to pass through ID Site or the IdP back to your application through the callback. It is up to the developer to serialize/deserialize this value
ash No Specifies a link to an Account Store to attempt to authenticate against.
onk No The string representing the nameKey for an Organization that is an Account Store for your application. This is used for multitenant applications that use SAML.
Step 2: Initiate the flow

Once the JWT is generated by your server, you initiate the SAML flow by sending a GET to the value found in the ssoInitiationEndpoint, which is /v1/applications/$APPLICATION_ID/saml/sso/idpRedirect along with the JWT you just generated:

GET /v1/applications/$APPLICATION_ID/saml/sso/idpRedirect?accessToken=$GENERATED_JWT HTTP/1.1
Host: api.stormpath.com
Content-Type: application/json;charset=UTF-8
Step 3: Redirection

This GET will result in a redirection to the IdP Login URL that you specified during configuration:

HTTP/1.1 302 Redirect
Location: https://idp.whatever.com/saml2/sso/login?SAMLRequest=fZFfa8IwFMXfBb9DyXvaJtZ1BqsURRC2Mabbw95ivc5Am3TJrXPffmmLY3%2F...
Step 4: Identity Provider Login

At this point the IdP will render their login page, and the user will authenticate.

Step 5: Redirect to Assertion Consumer Service URL

After authentication the user is redirected back to the Assertion Consumer Service URL that is found in the Service Provider Metadata. At this point, an Account will either be retrieved (if it already exists) or created (if it doesn’t exist already).

Note

Account matching is done on the basis of the returned email address.

Step 6: Stormpath Response with JWT

The user will now be redirected by Stormpath back to your Application along with a JSON Web Token.

HTTP/1.1 302 Redirect
Location: https://myapplication.com/whatever/callback?jwtResponse=$RESPONSE_JWT

For more information about what is contained in this token, please see above.

At this point your user is authenticated and able to use your app.

4.6. Using Multi-Factor Authentication (MFA)

At a minimum, an Account in Stormpath requires at least one authentication factor, which is the password. However, if you would like to include additional security then Stormpath supports the creation of additional authentication factors on an Account. Currently, the additional factors are:

  • SMS message to a phone
  • Google Authenticator

The multi-factor authentication process works as follows with text messages:

  1. An additional Factor of type SMS is added to an Account.
  2. A Challenge is created which includes a message.
  3. The message is sent to the phone number found in the Factor with a one-time numerical code.
  4. If that code is then passed back to the appropriate Challenge within a sufficient time window, you will get back either a SUCCESS or FAILED response.

With Google Authenticator, the flow is only slightly different:

  1. An additional Factor of type google-authenticator is added to an Account.
  2. The new Factor has a secret and a Base64-encoded QR code, both of which can be used to add it to the Google Authenticator (or similar) app.
  3. At any time, you can send the code from the Authenticator app to Stormpath’s /challenges endpoint, at which point you will get back either a SUCCESS or FAILED response.

Note

Multi-Factor Authentication is only available with paid Stormpath plans. For more information please see Stormpath’s Pricing Page.

4.6.1. Enrolling an Additional Factor

Enrolling an additional authentication factor always happens separate from Account creation, so the process is the same regardless of whether you are creating a factor for an existing Account or one that was just created. For this example, we will create a new Account in Stormpath, then add an additional Factor resource to it.

First, you create the Account:

POST /v1/applications/1gk4Dxzi6o4PbdleXaMPLE/accounts HTTP/1.1
Host: api.stormpath.com
Authorization: Basic MlpG...
Content-Type: application/json
Cache-Control: no-cache

{
    "givenName": "Joe",
    "surname": "Factorman",
    "username": "factorman",
    "email": "joe.factorman@stormpath.com",
    "password":"Changeme1"
}

Adding an SMS Factor

To add an additional SMS Factor to this Account, you send the following request:

POST /v1/accounts/5IvkjoqcYNe3TYMExample/factors HTTP/1.1
Host: api.stormpath.com
Authorization: Basic MlpG...
Content-Type: application/json

{
  "type":"SMS",
  "phone": {
    "number": "2675555555"
  }
}

You will then get back the response:

{
  "href": "https://api.stormpath.com/v1/factors/29b9PiAaWqr9Hanexample",
  "type": "SMS",
  "createdAt": "2016-09-22T21:34:00.881Z",
  "modifiedAt": "2016-09-22T21:34:00.881Z",
  "status": "ENABLED",
  "verificationStatus": "UNVERIFIED",
  "account": {
      "href": "https://api.stormpath.com/v1/accounts/5IvkjoqcYNe3TYMExample"
  },
  "challenges": {
      "href": "https://api.stormpath.com/v1/factors/29b9PiAaWqr9Hanexample/challenges"
  },
  "phone": {
      "href": "https://api.stormpath.com/v1/phones/29b9PeqVcGYAelhExample"
  },
  "mostRecentChallenge": null
}

For now the verificationStatus is UNVERIFIED and the link to the mostRecentChallenge is null. If you were to send a challenge to this Factor, the mostRecentChallenge link would be populated. If that challenge was successful, the verificationStatus would change to VERIFIED.

For more information about the Factor resource, see the Reference chapter.

Adding a Google Authenticator Factor

To add an additional Google Authenticator Factor to this Account, you must send the following request:

POST /v1/accounts/5IvkjoqcYNe3TYMYExample/factors HTTP/1.1
Host: api.stormpath.com
Content-Type: application/json
Authorization: Basic MlpG...

{
  "type":"google-authenticator",
  "accountName": "jakub@stormpath.com",
  "issuer": "Example App"
}

Note

Although the issuer field is not required, it is recommended because it will make a name show up under the code in the Google Authenticator (or similar) app.

You will then get back the response:

{
  "href": "https://api.stormpath.com/v1/factors/46EZpOuefEEooFlexample",
  "type": "google-authenticator",
  "createdAt": "2016-09-22T21:42:57.636Z",
  "modifiedAt": "2016-09-22T21:42:57.636Z",
  "status": "ENABLED",
  "accountName": "jakub@stormpath.com",
  "issuer": null,
  "secret": "OP7JZ[...]LAV",
  "keyUri": "otpauth://totp/jakub%40stormpath.com?secret=OP7JZ[...]LAV",
  "base64QRImage": "iVBOR[...]SuQmCC",
  "verificationStatus": "UNVERIFIED",
  "account": {
      "href": "https://api.stormpath.com/v1/accounts/5IvkjoqcYNe3TYMYExample"
  },
  "mostRecentChallenge": null,
  "challenges": {
      "href": "https://api.stormpath.com/v1/factors/46EZpOuefEEooFlexample/challenges"
  }
}

For more information about the Factor resource, see the Reference chapter.

The user now needs to get this information into their Google Authenticator (or similar) application. The easiest way to do that is to use their app to scan a QR code.

Stormpath makes this easy by giving you the QR Code in the base64_q_r_image field of the Google Authenticator Factor.

You can now take this string and turn it into a QR Code image:

  • You could use a QR Code Library, such as QRCode.js
  • Or you could generate the image yourself, using an <img> tag or CSS. For examples of both, see here.

Once the image is generated, the user will scan it into their Authenticator app. If you ask them for a code, they will go into the app and find the code for your application. For information about what happens with this code, see below.

Note

Many authenticator apps are compatible with Google Authenticator QR codes. Apps we like include Authy and Duo Mobile.

4.6.2. Challenging a Factor

At this point in the example you have a brand new Account with two additional Factors.

If you were to send a GET to the Account’s /factors endpoint, you will see them:

{
  "href": "https://api.stormpath.com/v1/accounts/5IvkjoqcYNe3TYMYiX98vc/factors",
  "offset": 0,
  "limit": 25,
  "size": 2,
  "items": [
    {
      "href": "https://api.stormpath.com/v1/factors/29b9PiAaWqr9Hanexample",
      "type": "SMS",
      "createdAt": "2016-09-22T21:34:00.881Z",
      "modifiedAt": "2016-09-22T21:34:00.881Z",
      "status": "ENABLED",
      "verificationStatus": "UNVERIFIED",
      "account": {
          "href": "https://api.stormpath.com/v1/accounts/5IvkjoqcYNe3TYMExample"
      },
      "challenges": {
          "href": "https://api.stormpath.com/v1/factors/29b9PiAaWqr9Hanexample/challenges"
      },
      "phone": {
          "href": "https://api.stormpath.com/v1/phones/29b9PeqVcGYAelhExample"
      },
      "mostRecentChallenge": null
    },
    {
      "href": "https://api.stormpath.com/v1/factors/46EZpOuefEEooFlexample",
      "type": "google-authenticator",
      "createdAt": "2016-09-22T21:42:57.636Z",
      "modifiedAt": "2016-09-22T21:42:57.636Z",
      "status": "ENABLED",
      "accountName": "jakub@stormpath.com",
      "issuer": null,
      "secret": "OP7JZ[...]LAV",
      "keyUri": "otpauth://totp/jakub%40stormpath.com?secret=OP7JZ[...]LAV",
      "base64QRImage": "iVBOR[...]SuQmCC",
      "verificationStatus": "UNVERIFIED",
      "account": {
          "href": "https://api.stormpath.com/v1/accounts/5IvkjoqcYNe3TYMYExample"
      },
      "mostRecentChallenge": null,
      "challenges": {
          "href": "https://api.stormpath.com/v1/factors/46EZpOuefEEooFlexample/challenges"
      }
    }
  ]
}

You can now challenge each of these factors.

Challenging After Factor Creation

Note

This example covers challenging Factors that have already been created. To see an example of how to challenge a Factor at the same time as you are creating it, please see below.

Challenging an SMS Factor

To challenge an SMS Factor, you send a request like this, with or without specifying a message.

POST /v1/factors/3WPF5Djir0Wg5FtPoJCPbo/challenges HTTP/1.1
Host: api.stormpath.com
Authorization: Basic MlpG...
Content-Type: application/json
Cache-Control: no-cache

{
  "message":"For the sake of example, your code is ${code}."
}

This operation will automatically cause the factor to be challenged, meaning that the user will receive a code as soon as this operation is executed.

Note

If you do not specify a message, then Stormpath will just send the default message: "Your verification code is ${code}".

In response to this request you would get back a Challenge:

{
  "href": "https://api.stormpath.com/v1/challenges/70xfDsguePApNdnExample",
  "createdAt": "2016-09-22T22:35:44.799Z",
  "modifiedAt": "2016-09-22T22:35:44.800Z",
  "status": "CREATED",
  "message": "For the sake of example, your code is ${code}.",
  "account": {
      "href": "https://api.stormpath.com/v1/accounts/5IvkjoqcYNe3TYMExample"
  },
  "factor": {
      "href": "https://api.stormpath.com/v1/factors/3WPF5Djir0Wg5FtPoJCPbo"
  }
}

For more information about this Challenge resource, see the Reference chapter.

The resulting SMS would look like this:

SMS Challenge Message

This code will remain valid for 300 seconds (5 minutes).

Next, you must collect this code from the user.

Note

The code has to be sent to the correct Challenge href. If your application is stateless, you could include the Challenge href in a hidden field on your form. If your application has a session, then you will want to attach the Challenge href to that session.

Once you have the code, you submit it to the same Challenge you created above:

POST /v1/challenges/70xfDsguePApNdnExample HTTP/1.1
Host: api.stormpath.com
Authorization: Basic MlpG...
Content-Type: application/json

{
  "code":"633559"
}

And then you would get back the response:

{
  "createdAt": "2016-09-22T22:35:44.799Z",
  "modifiedAt": "2016-09-22T22:39:06.822Z",
  "href": "https://api.stormpath.com/v1/challenges/70xfDsguePApNdnExample",
  "message": "For the sake of example, your code is ${code}.",
  "factor": {
      "href": "https://api.stormpath.com/v1/factors/3WPF5Djir0Wg5FtPoJCPbo"
  },
  "account": {
      "href": "https://api.stormpath.com/v1/accounts/5IvkjoqcYNe3TYMYiX98vc"
  },
  "status": "SUCCESS"
}

If you had sent the wrong code, the status would instead be FAILED.

For a full list of Challenge statuses, please see the Reference chapter.

Note

You could also pass the Challenge href and the code to Stormpath and get back an OAuth 2.0 Access Token. For more information about this see Generating an OAuth 2.0 Access Token.

Challenging a Google Authenticator Factor

When you created the Google Authenticator Factor, you also generated a QR Code image for your user to scan into their app. Challenging this factor now only requires you to prompt the user to enter in the code from their Authenticator app.

Unlike the SMS challenge process, the Google Authenticator challenge process does not require you to create a Challenge resource. Instead, the Challenge is created and verified in one step. For more information about the Challenge resource, see the Reference chapter.

Once you have collected the code from the user, submit it:

POST /v1/factors/4KOeu7ypRQI8Bpk2org7tk/challenges HTTP/1.1
Host: api.stormpath.com
Authorization: Basic MlpG...
Content-Type: application/json

{
  "code":"786393"
}

If the code is correct, Stormpath will now simultaneously create the Challenge resource and set its status to SUCCESS, then return it back to you.

{
  "href": "https://api.stormpath.com/v1/challenges/EGDIpcgffklwo6HywNzTw",
  "createdAt": "2016-09-22T22:50:59.241Z",
  "modifiedAt": "2016-09-22T22:50:59.241Z",
  "status": "SUCCESS",
  "account": {
      "href": "https://api.stormpath.com/v1/accounts/5IvkjoqcYNe3TYMYExample"
  },
  "factor": {
      "href": "https://api.stormpath.com/v1/factors/4KOeu7ypRQI8Bpk2org7tk"
  }
}

Challenging During Factor Creation

Note

For this example, we will use an SMS challenge. Challenging a Google Authenticator Factor during creation is not feasible because the user has to add the factor to their application before they can get a code.

To send a challenge at the same time as you create the SMS Factor, you need to POST to the Account’s /factors endpoint with the additional ?challenge=true parameter included. Then you must also add the challenge into the body of the JSON.

POST /v1/accounts/5IvkjoqcYNe3TYMExample/factors?challenge=true HTTP/1.1
Host: api.stormpath.com
Authorization: Basic MlpG...
Content-Type: application/json

{
  "type":"sms",
  "phone": {
    "number": "2675555555"
  },
  "challenge": {
    "message": "Welcome to the Example! Your authorization code is ${code}"
  }
}

You are telling Stormpath to send an SMS to the phone number 267-555-5555 along with the message "Welcome to the Example! Your authorization code is ${code}". The placeholder ${code} will be replaced with a one-time password generated using the HOTP algorithm.

Note

If you wanted Stormpath to send the default message, then you could just not include the challenge object or its message at all.

Challenging a Factor After Login

The first step is to authenticate the user.

In the case of REST, this means a POST to your Application resource’s /loginAttempts endpoint. In this case it will be very helpful to also include expand=account.

POST /v1/applications/1gk4Dxzi6o4PbdleXaMPLE/loginAttempts?expand=account HTTP/1.1
Host: api.stormpath.com
Authorization: Basic MlpG...

{
  "type": "basic",
  "value": "amFrdWIrbWZhdGVzdExamplebXBhdGguY29tOkNoYW5nZW1lMQ=="
}

If authentication is successful, you will get back the Account:

{
  "href": "https://api.stormpath.com/v1/accounts/5IvkjoqcYNe3TYMExample",
  "username": "factorman",
  "email": "jakub+factorman@stormpath.com",
  "givenName": "Joe",
  "middleName": null,
  "surname": "Factorman",
  "fullName": "Joe Factorman",
  "status": "ENABLED",
  "...": "...",
  "factors": {
      "href": "https://api.stormpath.com/v1/accounts/5IvkjoqcYNe3TYMExample/factors"
  }
}

Next, you will need to retrieve the Account’s factors collection:

GET /v1/accounts/5IvkjoqcYNe3TYMExample/factors HTTP/1.1
Host: api.stormpath.com
Authorization: Basic MlpG...
Content-Type: application/json
{
  "href": "https://api.stormpath.com/v1/accounts/5IvkjoqcYNe3TYMYiX98vc/factors",
  "offset": 0,
  "limit": 25,
  "size": 1,
  "items": [
    {
      "href": "https://api.stormpath.com/v1/factors/3WPF5Djir0Wg5FtPoJCPbo",
      "type": "SMS",
      "createdAt": "2016-09-22T22:11:03.768Z",
      "modifiedAt": "2016-09-22T22:42:39.822Z",
      "status": "ENABLED",
      "verificationStatus": "VERIFIED",
      "account": {
          "href": "https://api.stormpath.com/v1/accounts/5IvkjoqcYNe3TYMYiX98vc"
      },
      "challenges": {
          "href": "https://api.stormpath.com/v1/factors/3WPF5Djir0Wg5FtPoJCPbo/challenges"
      },
      "phone": {
          "href": "https://api.stormpath.com/v1/phones/3WManCalQOcizNsHjeeiHk"
      },
      "mostRecentChallenge": {
          "href": "https://api.stormpath.com/v1/challenges/6kgEJR5Cr3pNh131i7b6wm"
      }
    }
  ]
}

You would then send a POST to the challenges collection which would generate a new Challenge and send an SMS message to the number specified in the Factor’s Phone resource.