title | source_url | category | version | tags | description |
---|---|---|---|---|---|
Configuration of Web Token Authentication |
Development |
v7.0.0 |
webtoken jwt authentication sn7 |
Configuration of Web Token Authentication |
In a sensenet web application (on all instances) you need to configure the token authentication in the web.config
file.
Find the SymmetricKeySecret
parameter in the tokenAuthentication
section of the sensenet section group. Give it a value of random string (16 - 64 in length) in order to make the authentication work.
All your instances in the NLB should have the same value as their SymmetricKeySecret. Without this your authentication wouldn't work. Also very important to keep this random string a secret, otherwise someone can exploit it as a security breach. It is a good practice to encrypt the whole tokenAuthentication section in the web.config file.
Example of token authentication configuration settings:
<sensenet>
...
<tokenAuthentication>
<add key="SymmetricKeySecret" value="<random secret string>" />
</tokenAuthentication>
...
</sensenet>
There are some other parameters in the tokenAuthentication and security section that you can update to alter the behavior of the token authentication:
<tokenAuthentication>
<add key="SymmetricKeySecret" value="<random secret string>" />
<add key="Audience" value="client" />
<add key="Issuer" value="sensenet" />
<add key="Subject" value="auth" />
<add key="AccessLifeTimeInMinutes" value="5" />
<add key="RefreshLifeTimeInMinutes" value="1440" />
<add key="ClockSkewInMinutes" value="1"/>
</tokenAuthentication>
<security>
...
<add key="DefaultUltimateLogout" value="true"/>
...
</security>
Audience, Issuer, Subject: environment constants to include in the token (further information at the end of the Token Authentication Protocol part)
AccessLifeTimeInMinutes: the time span within the access token is valid from its creation
RefreshLifeTimeInMinutes: the time span within the refresh token is valid from its creation
ClockSkewInMinutes: the possible maximum difference in actual times between servers
DefaultUltimateLogout: the default behavior at logout if ultimateLogout argument (see later) is not provided (true: executes an ultimate logout, false(default): simple logout). Note that if you provide 'true' here, the system will always perform an ultimate logout, regardless of the parameter provided by the client.
The token authentication needs a username and password pair for its first move. After it was given and the user was successfully identified, the service generates an access token and a refresh token and sends it to the client. The client can use the access token to get to the content allowed only for authenticated users. Every token has its expiration time, so when the access token is expired the client cannot access protected content. The client has to use the refresh token to obtain a new access token. When it is received, the client can use it to access content again. The refresh token could be expired too. In that case the client has to re-authenticate with a username and password and regain access to protected content. After a user finished his work, logs out. This deletes the obtained credentials. There are two ways of logging out: simple, when the user's logout deletes the local credentials only and ultimate, when the user's logout prevents the user to access protected content from any session on any device until he is logged in again.
Steps of an authentication process from the clients' point of view:
- Login with username and password by basic authentication on the login endpoint
- Receive a new access token and refresh token
- Access content using the access token
- Logout using the access token
Steps of a token refresh process from the clients' point of view:
- Send the refresh token to the refresh endpoint
- Receive a new access token
- Access content using the access token
- Logout using the access token
All the communication are sent through SSL (https). The used cookies are all HtmlOnly and Secure. There are two types of communication: header marked and uri marked (without header mark). Either of them can be chosen freely by a client developer. However, the two could be mixed, but we advice to choose one and stick to it.
figure 1:web token authentication protocol
LoginRequest with header mark:
uri:
https://<yourhost>/<indifferentpath>
headers:
X-Authentication-Action: TokenLogin
Authorization: Basic <base64CodedCredentials>
LoginRequest with uri mark:
uri:
https://<yourhost>/sn-token/login
headers:
Authorization: Basic <base64CodedCredentials>
LoginResponse:
cookies:
Set-Cookie: rs=<refreshSignature>
Set-Cookie: as=<accessSignature>
body:
{"access":"<accessHeadAndPayload>", "refresh":"<refreshHeadAndPayload>"}
Simple LogoutRequest with header mark:
uri:
https://<yourhost>/<indifferentpath>
headers:
X-Authentication-Action: TokenLogout
X-Access-Data: <accessHeadAndPayload>
cookies:
Cookie: as=<accessSignature>
Cookie: ahp=<accessHeadAndPayload>
Cookie: rs=<refreshSignature>
Simple LogoutRequest with uri mark:
uri:
https://<yourhost>/sn-token/logout
headers:
cookies:
Cookie: as=<accessSignature>
Cookie: ahp=<accessHeadAndPayload>
Cookie: rs=<refreshSignature>
Ultimate LogoutRequest with header mark:
verb (mandatory): POST
uri:
https://<yourhost>/<indifferentpath>
headers:
Content-Type: application/x-www-form-urlencoded
X-Authentication-Action: TokenLogout
X-Access-Data: <accessHeadAndPayload>
cookies:
Cookie: as=<accessSignature>
Cookie: ahp=<accessHeadAndPayload>
Cookie: rs=<refreshSignature>
body:
ultimateLogout=true
Ultimate LogoutRequest with uri mark:
uri:
https://<yourhost>/sn-token/logout?ultimateLogout=true
headers:
cookies:
Cookie: as=<accessSignature>
Cookie: ahp=<accessHeadAndPayload>
Cookie: rs=<refreshSignature>
AuthenticatedServiceRequest with header mark:
uri:
https://<yourhost>/<contentpath>
headers:
X-Authentication-Action: TokenAccess
X-Access-Data: <accessHeadAndPayload>
cookies:
Cookie: as=<accessSignature>
Cookie: ahp=<accessHeadAndPayload>
Cookie: rs=<refreshSignature>
AuthenticatedServiceRequest without header mark:
uri:
https://<yourhost>/<contentpath>
headers:
X-Access-Data: <accessHeadAndPayload>
cookies:
Cookie: rs=<refreshSignature>
Cookie: ahp=<accessHeadAndPayload>
Cookie: as=<accessSignature>
UnauthenticatedServiceRequest with header mark:
uri:
https://<yourhost>/<contentpath>
headers:
X-Authentication-Action: TokenAccess
X-Access-Data: <expiredAccessHeadAndPayload>
cookies:
Cookie: as=<expiredAccessSignature>
Cookie: ahp=<expiredAccessHeadAndPayload>
Cookie: rs=<refreshSignature>
UnauthenticatedServiceRequest without header mark:
uri:
https://<yourhost>/<contentpath>
headers:
cookies:
Cookie: as=<expiredAccessSignature>
Cookie: ahp=<expiredAccessHeadAndPayload>
Cookie: rs=<refreshSignature>
ServiceResponse:
body:
<contentData>
RefreshRequest with header mark:
uri:
https://<yourhost>/<indifferentpath>
headers:
X-Authentication-Action: TokenRefresh
X-Refresh-Data: <refreshHeadAndPayload>
cookies:
Cookie: as=<expiredAccessSignature>
Cookie: ahp=<expiredAccessHeadAndPayload>
Cookie: rs=<refreshSignature>
RefreshRequest with uri mark:
uri:
https://<yourhost>/sn-token/refresh
headers:
X-Refresh-Data: <refreshHeadAndPayload>
cookies:
Cookie: as=<expiredAccessSignature>
Cookie: ahp=<expiredAccessHeadAndPayload>
Cookie: rs=<refreshSignature>
RefreshResponse:
cookies:
Set-Cookie: as=<accessSignature>
Set-Cookie: ahp=<accessHeadAndPayload>
body:
{"access":"<accessHeadAndPayload>"}
<200>:
HTTP response with status 200 (OK). On the diagram it is used to sign an empty response in case of a not authenticated request. It is important that sensenet does not throw an exception here.
<401>:
HTTP response with status 401 (Unauthorized). On the diagram it is used to sign a response to an unsuccessful login, logout or refresh request.
Authorization: this header is a standard HTTP header and tells the service, that a client would like to authenticate. Its value always begins with "Basic ", that signs a basic type authentication requires a valid username and password.
X-Access-Data: this header tells the service, that a client tries to access a content with a token. Its value is an access token head and payload.
X-Authentication-Action: this header tells the service in case of header marked communication, that a token authentication action is requested. Its value can be TokenLogin
, TokenLogout
, TokenAccess
, TokenRefresh
.
X-Refresh-Data: this header tells the service, that a client tries to refresh its expired access token. Its value is a refresh token head and payload.
as, ahp, rs: technical HttpOnly and Secure cookies for token authentication. They are emitted by token authentication service. The client does not need them and they are not subjects of change.
<refreshSignature>, <accessSignature>
: signature strings used by the authentication service.
<accessHeadAndPayload>, <refreshHeadAndPayload>
: base64 and URL encoded strings.
The access head and payload are the public part of a token, that consists of two parts separated by a full stop. The first one is a technical like header that you do not have to care about. The second one - the payload - contains claims about the authenticated user and about some authentication concerning data. Once the payload has been decoded from base64 it will be a string representation of a JSON object, so it can be easily used in Javascript.
Example of a typical payload:
{
"iss":"sensenet-token-service",
"sub":"sensenet",
"aud":"client",
"exp":1490577801,
"iat":1490577501,
"nbf":1490577501,
"name":"Joe"
}
iss: issuer
identifies the principal that issued the token
sub: subject
identifies the principal that is the subject of the token
aud: audience
identifies the recipients that the token is intended for
exp: expiration time
identifies the time whereupon the token will not be accepted
iat: issued at
identifies the time when the token was issued
nbf: not before
identifies the time before that the token can not be accepted
name: name
identifies the name of the user whom the token was issued to
The iss, sub, aud
claims can be configured and remains the same unless you change them in the web.config. The other claims dynamically change on new token creation.
Once the client application has got the access token and the refresh token, it has to persist them preferably in some local browser storage for later usage. However the refresh token also contains the same claims as the access token, its claims - at least iat, nbf
and exp
- have different values. It happens because of their different use. An access token will be immediately valid and accepted after its creation, but the refresh token is not. The refresh token will be valid and accepted by the service only when the access token is expired. Therefore the client should extract the expiration time of the tokens into an application lifetime variable and constantly check it when the client try to access a content. Content access request have to include the access token into the according HTTP header (specified as AuthenticatedServiceRequest
earlier). In case when the access tokens expiration check results true the client must check the refresh token's expiration. If this results false, the client have to send a RefreshRequest
(specified earlier) to the service. A RefreshRequest
will reply with a new access token, that must replace the old one. If the check results true, the client cannot access protected content unless sending a new LoginRequest
to the service with the username and password of the user. Because of the sensitive nature of user's credentials, we do not recommend the client to persist them. When the client wants to log out, a LogoutRequest
must be sent and if it responds in a status 200 the client stored credentials should be deleted. As the lifetime of both the access and refresh tokens can be changed in the service's configuration, it is very important to choose them wisely to support the fluent communication between the two part. Wrong settings can disrupt efficiency of turn arounds and slow down the whole system.
You can learn more about how you can use Web Token Authentication in sn-client-js from the following tutorial.