Example - Username and password based authentication#
To illustrate the Hypermedia Authentication API (HAAPI), namely the Curity custom media type, we present in this section the requests and responses for a complete flow, using a username-password authenticator. To simplify the reading, only some of the request and response headers are shown.
We start the flow with an OAuth authorization request, containing the following additions to what is already defined by OAuth 2.0:
- The
Acceptheader contains the Curity media type (application/vnd.auth+json); - The client needs to use the HAAPI access token, using DPoP for proof-of-possession:
GET /dev/oauth/authorize?client_id=client-one&response_type=code&... HTTP/1.1
Host: example.com
Accept: application/vnd.auth+json
Authorization: DPoP ey...Yw
DPoP: ey..BA
The response is an API redirect to the authentication service:
HTTP/1.1 200 OK
Content-Type: application/vnd.auth+json
{
"type": "redirection-step",
"actions":[
{
"template": "form",
"kind":" redirect",
"model": {
"href": "https://example.com/dev/authn/authenticate?serviceProviderId...",
"method": "GET"
}
}
]
}
There are a few things to highlight about this response:
- The top level
typehas theredirection-stepvalue, informing the client that this in an intermediate step in the flow, more specifically an redirection step. - There is a single action, making it clear for the client what needs to be done next.
- The action has
templateset toform, meaning that themodelobject will contain a form, i.e., information on how to perform a request. - The
kindis set toredirectmeaning that this response represents a redirect, i.e, the client can perform the next request without requesting information from the user.
Following this, the client performs the following request.
Notice that this request needs to use a new DPoP proof token, namely with the htu claim set to the request’s target URI:
GET /dev/authn/authenticate?serviceProviderId... HTTP/1.1
Host: example.com
Accept: application/vnd.auth+json
Authorization: DPoP ey...Yw
DPoP: ey...IQ
The response contains a selection representation:
HTTP/1.1 200 OK
Content-Type: application/vnd.auth+json
{
"type": "authentication-step",
"actions": [
{
"template": "selector",
"kind": "authenticator-selector",
"title": "Select Authentication Method",
"model": {
"options": [
{
"template": "form",
"kind": "select-authenticator",
"title": "A standard SQL backed authenticator",
"properties": {
"authenticatorType": "html-form"
},
"model": {
"href": "/dev/authn/authenticate/htmlSql",
"method": "GET"
}
},
{
"template": "form",
"kind": "select-authenticator",
"title": "A standard LDAP backed authenticator",
"properties": {
"authenticatorType": "html-form"
},
"model": {
"href": "/dev/authn/authenticate/htmlLdap",
"method": "GET"
}
},
{
"template": "form",
"kind": "select-authenticator",
"title": "bankid1",
"properties": {
"authenticatorType": "bankid"
},
"model": {
"href": "/dev/authn/authenticate/bankid1",
"method": "GET"
}
}
]
}
}
]
}
Notice:
- The
typeisauthentication-step, informing the client application that we are in the middle of authenticating the user. - Again, there is a single top-level action, making it clear to the client what needs to be done next.
- The action’s
templateis nowselectorand notformas in the previous response. This informs the client that themodelobject describes a sequence of nested actions and not a form. The action’skindprovides additional information about the selection, namely that it is an authenticator selector. - As any other action, the selector has a title providing information about the action’s purpose.
- The selector is represented by a model object with an
optionsarray, describing each one of the available options.
The selector title is UI-ready, meaning that it contains a string ready to be shown in the user interface.
This string is computed by the server using the existing localization subsystem, which supports internationalization and string customization.
Each option item contains a nested action, describing the option and what to do if it is selected by the user.
- The
templatefield states that it is a form, i.e., a representation of a request to be made to the server. - The
kindfield states that performing this action will select an authenticator. - The
titlefield has an UI-ready description of the authenticator. - The
propertiesfield has additional information about the action. In this case, it contains a string with the authenticator type.
The client presents these authentication selection options to the user, using the title and authenticatorType to enrich the user experience.
The user selects one of them and the client performs the HTTP request described by the form’s model, i.e., does a GET request to the URI defined in the href field:
GET /dev/authn/authenticate/htmlSql HTTP/1.1
Host: example.com
Accept: application/vnd.auth+json
Authorization: DPoP ey...Yw
DPoP: ey...Mg
The response contains a username and password based authentication form:
HTTP/1.1 200 OK
Content-Type: application/vnd.auth+json
{
"type": "authentication-step",
"actions": [
{
"template": "form",
"kind": "login",
"title": "Login",
"model": {
"href": "/dev/authn/authenticate/htmlSql",
"method": "POST",
"type": "application/x-www-form-urlencoded",
"actionTitle": "Login",
"fields": [
{
"name": "userName",
"type": "username",
"label": "Username"
},
{
"name": "password",
"type": "password",
"label": "Password"
}
]
}
}
],
"links":[
{
"href": "/dev/authn/authenticate/htmlSql/forgot-password",
"rel": "forgot-password",
"title": "Forgot your password?"
},
{
"href": "/dev/authn/authenticate/htmlSql/forgot-account-id",
"rel": "forgot-account-id",
"title": "Forgot your username?"
},
{
"href": "/dev/authn/register/create/htmlSql",
"rel": "register-create",
"title": "Create account"
}
]
}
The type is still authentication-step and there is still a single action.
The action’s template is form, meaning that the action’s model describes a form.
The returned representation also contains a links top-level field, informing the client that there are some related resources to this step, namely the ones to redefine the password, recover the username, or register a new account.
Each link is represented by:
- An UI-ready
titlestring that can be used in the UI when the link is shown. - The
relidentifier, defining the relationship between the current resource (i.e. the username-password form) and the target resource (e.g. the forgot password resource). - The
hreffield with the target resource URI.
The form is described in the model field and has:
-
The
hreffield with the URI to where the form should be submitted to. -
The
methodfield with the HTTP method to use (e.g.POSTorGET). -
The
typefield with the identifier of the media type to use on the submission. A value ofapplication/x-www-form-urlencodedmeans that the format to use is the same as the one used by browsers when submitting an HTML form by default. -
The
actionTitleprovides an additional UI-ready string that can be used when presenting the form in the UI. Thetitledescribes the overall form, whileactionTitlecan be used on the submit button. -
Finally, the
fieldsarray describes the form fields, where each one is described by:- The field’s
type(e.g.usernameorpassword). - The field’s
nameto use on the HTTP request when submitting the form. - The field’s UI-ready
labelto use when presenting the field input control on the UI.
- The field’s
The client uses the HAAPI representation to drive an UI that requests the username and password from the user.
After collecting this information, the client performs a POST, containing the userName and password fields, using the application/x-www-form-urlencoded media type, as described by the model field:
POST /dev/authn/authenticate/htmlSql HTTP/1.1
Host: example.com
Accept: application/vnd.auth+json
Content-Type: application/x-www-form-urlencoded
Authorization: DPoP ey...Yw
DPoP: ey...Kg
userName=...&password=...
If the provided username and password are correct and the authentication process is completed, then HAAPI responds with a POST-based redirect back to the authorization service:
HTTP/1.1 200 OK
Content-Type: application/vnd.auth+json
{
"type": "redirection-step",
"actions": [
{
"template": "form",
"kind": "redirect",
"model": {
"href": "/dev/oauth/authorize?...",
"method": "POST",
"type": "application/x-www-form-urlencoded",
"fields": [
{
"name": "token",
"type": "hidden",
"value": "mG...2q"
},
{
"name": "state",
"type": "hidden",
"value": "R_...jQ"
}
]
}
}
]
}
The client identifies this as a redirect because:
- The representation
typeisredirection-stepand there is a single action. - The action’s template is
formand thekindisredirect. - All the form’s fields are of
hiddentype, meaning that all the information required to submit the form is already available to client.
This means that the action can be performed without any user interaction:
POST /dev/oauth/authorize?... HTTP/1.1
Host: example.com
Accept: application/vnd.auth+json
Content-Type: application/x-www-form-urlencoded
Authorization: DPoP ey...gw
DPoP: ey...Kg
token=...&state=...
The final response contains the authorization response information:
HTTP/1.1 200 OK
Content-Type: application/vnd.auth+json
{
"type": "oauth-authorization-response",
"properties": {
"code": "bU...oQ",
"state": "...",
"iss": "https://example.com/dev/oauth/anonymous"
},
"links": [
{
"href": "https://client.example.net/client-callback?code\u003dbU...oQ\u0026state\u003d...",
"rel": "authorization-response"
}
]
}
The client should detect that this represents an authorization response by the type equal to oauth-authorization-response.
In this case the properties object contains the standard OAuth authorization response fields, in this case code (the authorization code), state (the state string sent by the client on the authorization request) and iss (the issuer identifier of the authorization server that created the authorization response).
From now on, the client should proceed exactly as defined by the OAuth 2.0 specifications, namely by doing a standard token request to exchange the authorization code for an access token.
Error representation#
The previous flows are happy-path examples, where all requests were successfully fulfilled. However, that is not always the case.
If the request cannot be fulfilled by the server, then the response will have a non-success status code and the payload will contain an error representation, using the application/problem+json format defined by RFC 7807.
For instance, if the submission of the username-password form is missing the password field, the response will be:
HTTP/1.1 400 Bad Request
Content-Type: application/problem+json
{
"type": "https://curity.se/problems/invalid-input",
"title": "Form Errors",
"invalidFields": [
{
"name": "password",
"reason": "invalidValue",
"detail": "You have to enter your password"
}
]
}
Both the type and the title fields follow the semantics defined in RFC 7807:
- The
typefield contains an unique identifier for the error type, in this case an input validation error. This type is an URI, however it may not be dereferenciable. - The
titlecontains an UI-ready string describing the error type.
The invalidFields is a custom representation field, containing information about the invalid or missing request fields.
The client will probably use this error representation to overlay some error information on the UI produced on the previous step, and collect newer information from the user to retry the form post.
The custom fields that may appear on an error representation, as well as their semantics, depends on the type value.
In the previous example, the invalidFields field meaning is contextualized by the https://curity.se/problems/invalid-input type.
As another example, if the username and passwords are both provided and don’t match, the response will be:
HTTP/1.1 400 Bad Request
Content-Type: application/problem+json
{
"type": "https://curity.se/problems/incorrect-credentials",
"title": "Incorrect credentials"
}
In this case, no field information is provided, namely for security reasons. However the different type provides information about this distinct error cause.
Finally, the following response illustrates yet a different error type, this one following an authorization request with an invalid response_type:
HTTP/1.1 400 Bad Request
Content-Type: application/problem+json
{
"type": "https://curity.se/problems/error-authorization-response",
"error": "unsupported_response_type",
"error_description": "Unknown or invalid response_type requested.",
"links": [
{
"href": "https://client.example.net/client-callback?error\u003dunsupported_response_type\u0026error_description\u003dUnknown+or+invalid+response_type+requested.\u0026state\u003d...",
"rel": "authorization-response"
}
]
}
In this case the type informs that this payload represents an authorization response.
The error and error_description are custom fields that follow the OAuth 2.0 authorization response semantics.
API-based redirects#
During the execution of a flow, HAAPI may need to redirect the client to different resources.
The use of the HTTP redirect mechanism (3xx status code and Location response header) is an obvious choice for this.
However, the use of per-request DPoP proof tokens prevents this since every HTTP request requires a newly-created proof token bound to the request target URI, including the requests triggered by a redirect. Due to this, API redirects are made using a 200 OK response and an action containing the target of the redirect.
Flow-state management#
During a flow, i.e. the sequence of requests and responses required to perform authorization and authentication, the client needs to always send the on Session-Id header the last identifier received via the Set-Session-Id header.
See Flow State Management for more information.
Note that, for visualization simplicity reasons, the previous HTTP messages excerpts do not include these headers.
Metadata#
The HAAPI responses may also include the metadata top-level field, not shown in the previous examples, with extra information about the request processing context.
A client doesn’t need to understand this information to be able to perform a successful flow, however it can be used to provide richer user experiences.
As an example, the representation for the username-password form can have the following metadata fields:
HTTP/1.1 200 OK
Content-Type: application/vnd.auth+json
{
"metadata": {
"templateArea": "html1",
"viewName": "authenticator/html-form/authenticate/get"
},
"type": "authentication-step",
"actions": ...
}
- The
templateAreafield contains the name of template area selected by the server for the current client and authenticator. - The
viewNamefield contains the template name set by the request handler. For HAAPI requests, this uniquely identifies the representation function, i.e., the function that maps the response model defined by the handler into the HAAPI representation.