Version: 1.4.9This documentation is for Spitfire 1.4.9.
Login & token
When you test a protected API you do not have to add the token to every step by hand. In Login & token you define the login request once; Spitfire signs in for each VU, takes the token from the response and adds it to every request of the test. It signs in again by itself when the token expires or the server answers 401.
Define the login request
- On the Tests page
In Spitfire: /testsopen the test and click Edit. - In the Login & token card at the top of the Scenarios & steps tab, pick Login request.
- Type the method and the address, e.g.
POSTand:
{{base}}/auth/login- On the Body tab pick JSON and paste the credentials:
{"username": "{{users.email}}", "password": "{{users.password}}"}- Leave Source on JSON body; type the token's path in JSONPath (see the examples below):
data.access_token- With Variable left empty the token is
{{token}}. Sent in defaults to theAuthorization: Bearer {{token}}header. - Under Refresh leave On 401: sign in again + retry once ticked.
- Click Try the login.
How you know it worked: a green Signed in (200), token: appears next to the button with the token masked (e.g. eyJh…x9Qk (212)). If the path is wrong, a red box lists the paths the response has (e.g. $.token not found; the response has: data.access_token, data.expires_in); copy the right one from the list.
The login is measured in its own Login row of the results: its duration, error rate and how many times it ran show apart from the steps. The Login & token card on the run page counts the logins, the refreshes and the requests sent again after a 401/403.
Finding the token with JSONPath
You do not need the leading $.; data.token and $.data.token are the same.
| Response | Path |
|---|---|
{"token": "abc"} |
token |
{"data": {"access_token": "abc", "expires_in": 3600}} |
data.access_token |
{"sessions": [{"jwt": "abc"}]} |
sessions[0].jwt |
If the response is XML, pick XML body as Source and write an XPath:
//LoginResult/TokenIf the token comes in a response header, pick Response header and type its name (e.g. X-Auth-Token). If the server opens a session with a cookie, pick Cookie: each VU's cookie jar sends the cookie itself and no header is added.
How the token is sent
Fill Sent in and Format the way your API expects it:
| The API expects | Sent in | Name | Format |
|---|---|---|---|
| A bearer token | Header | Authorization |
Bearer {{token}} |
| A prefixed token | Header | Authorization |
Token {{token}} |
| An API key header | Header | X-API-Key |
{{token}} |
| A token in the query | Query parameter | access_token |
{{token}} |
The header shows as a read-only row in the Shared headers list (From Login & token). A step that writes a header of the same name itself keeps its own.
Refresh
- Sign in again every: each VU renews its token when this much time has passed (e.g. 15 min; at least 1 s).
- Lifetime (seconds) path: if the response gives a lifetime such as
expires_in, type its path (data.expires_in); the token is renewed 30 s before it runs out. - On 401 / 403: when a request answers that, the VU signs in again and sends the same request once more. If the second try answers 401 too, the step fails; there is no loop. A step with a check that expects 401 (an unauthorised access test, say) is not sent again.
- Refresh token path: the refresh token in the response goes into
{{token_refresh}}; you can use it in the login request's body.
When the login fails (an error status, or no token in the response) the VU's iteration ends and the VU waits a second before it tries again in its next iteration, so a broken login is not hammered under load.
Entering the token by hand
If you get the token elsewhere (a developer portal, say), pick I'll enter the token myself:
- Paste the token into Token.
- Set Sent in, Name and Format as in the table above.
- Save the test.
How you know it worked: Try it at the top of the page gets 200 from the steps; the token row shows in the Shared headers list.
A token entered by hand is not renewed. Update it here when it expires. The token is stored encrypted and only shown masked (its first and last 4 characters). It is not in the test JSON, an export or the CLI output. Paste a new one to replace it. A test copied or moved to another install needs the token entered again. A password and a client secret typed in an OpenAPI import are stored the same way, encrypted in secret_ variables.
Public steps
For steps that work without signing in, such as the home page, a health check or a sign-up:
- Open the step and go to the More tab.
- Tick Public · no login needed.
This step gets no token header, does not wait for the login, and a 401 on it does not sign in again (it is an ordinary failure). The login runs just before the VU's first step that is not public; when every step of the test is public, the login never runs. A ticked step shows a Public badge in its header.
When importing
- OpenAPI: the document's
securitySchemesare read. If you pick a login operation or an OAuth2 flow, the test comes with a Login & token block (for OAuth2 the lifetime pathexpires_inis set); if you give a token or an API key by hand, it comes in I'll enter the token myself mode. Operations withsecurity: [], and operations without security when the document has no global security, become public steps. - Postman: a collection-level bearer or API key auth moves into the block. Pre-request scripts that sign in or set a token are not run; the import screen shows a note about them, define the login request in the editor.
- HAR and Recorder: the recorded login comes in as a step. The editor notices a step that extracts a token used in other steps' headers and offers Use this step as the login: one click makes it the login request and removes the hand-written token headers from the steps.
Try it on the demo target
The demo target that comes with install.sh --demo has a login whose token runs out, so you can watch the refresh happen:
- Login request:
POSTand the demo target's address with/login?ttl=120, e.g.:
http://testtarget:8090/login?ttl=120- Body (JSON):
{"user": "ada", "password": "secret"}- JSONPath
token, Lifetime (seconds) pathexpires_in. - Add a step
GET http://testtarget:8090/itemsand run the test for 5 minutes.
How you know it worked: the Login & token card on the run page counts more than one login per VU. The token lives 120 s; /items answers 401 {"error": "token expired"} once it has run out. Clear the lifetime path to see the 401, the new login and the retried request instead.
ttl is in seconds (0 = never expires). Without ttl the token never runs out, as before. Whoever runs the demo target can make every token expire with spitfire-testtarget -token-ttl 2m.
Good to know
- The token is added to HTTP, GraphQL, WebSocket and SSE steps. Browser steps get no token; the page opens its own session.
- The login uses the data row of the iteration it runs in (
{{users.email}}); each VU signs in as its own user. - Tests that run on an external engine (JMeter, Gatling, Locust, k6) do not have this section.