cancel
Showing results for 
Search instead for 
Did you mean: 

question of limitation and expiration for rest api token

now I cannot login ltm via rest api , i thought the number of token of my account has reach the maximum .here is an error

login myhost fail b'{"code":401,"message":"remoteSender:http://localhost:8100/shared/authn/login, method:POST ","originalRequestBody":"{\\"username\\":\\"user\\",\\"loginProviderName\\":\\"tmos\\",\\"generation\\":0,\\"lastUpdateMicros\\":0}","referer":"ipaddress","restOperationId":124004534,"kind":":resterrorresponse"}'

here are my questions:

  1. is there any number limitation for number of rest api token a user can apply ? I can see some one say one user can only apply 100 tokens ,
  2. how to check the existing token by GUI, or cli since I cannot login device by rest api.
  3. how to take how long a token will expired ;
  4. is there any way to delete token;
1 ACCEPTED SOLUTION

Satoshi_Toyosa1
F5 Employee
F5 Employee

It is not obvious from the error message you provided, however, a usual error message you get from authorization error (e.g., incorrect password) is "message": "Authentication failed." The issue may be a bit deeper than you may think. Try restarting the iControl REST framework daemon by running 'tmsh restart sys service restjavad'. If the issue still persists, I recommend you to file a service ticket to F5 support.

View solution in original post

8 REPLIES 8

Hi, here is a routine I wrote for Ansible.

It´s validating an existing token before using it for the following tasks.

In case it is already invalid or it is expiring soon, a new token will be requested and stored for future use.

I use it i.e. for device onboarding, configuration and modification tasks.

Even if you don´t use Ansible it hopefully shows the REST calls applied.

It´s tested with TMOS v12-v15.

Cheers, Stephan

- name: request current token information no_log: "{{ logging_disabled }}" uri: validate_certs: no url: https://{{ inventory_hostname }}/mgmt/shared/authz/tokens/{{ device_info[inventory_hostname].token }} method: GET headers: X-F5-Auth-Token: "{{ device_info[inventory_hostname].token }}" status_code: - 200 - 401 register: token_info until: (token_info.status == 200) or (token_info.status == 401) retries: 90 delay: 10 when: (device_info is defined) and (device_info[inventory_hostname] is defined) and (device_info[inventory_hostname].token is defined) - name: debug current token debug: msg: - "auth status code: {{ token_info.status | default('undefined') }}" - "token valid for: {{ (token_info.json.expirationMicros | int - token_info.json.lastUpdateMicros | int) // 1000000 }} seconds" - "current token: {{ device_info[inventory_hostname].token | default('undefined') }}" when: (device_info is defined) and (device_info[inventory_hostname] is defined) and (device_info[inventory_hostname].token is defined) and (token_info.status != 401) - name: aquire new token on error or if token would expire soon no_log: "{{ logging_disabled }}" uri: validate_certs: no url: https://{{ inventory_hostname }}/mgmt/shared/authn/login method: POST body_format: json body: username: admin password: "{{ bigip_credentials_secure.admin }}" loginProviderName: tmos register: token_data until: token_data.status == 200 retries: 60 delay: 10 when: (token_info.status is not defined) or (token_info.status == 401) or ((token_info.status == 200) and (((token_info.json.expirationMicros | int - token_info.json.lastUpdateMicros | int) // 1000000) < 120)) - name: retrieve token from payload and store to data structure set_fact: device_info: "{{ device_info | default({}) | combine({inventory_hostname: {'token': token_data.json.token.token, 'expiration': token_data.json.token.expirationMicros}}, recursive=True) }}" when: (token_data is defined) and (token_data.status is defined) and (token_data.status == 200) - name: debug final token debug: msg: - "final token: {{ device_info[inventory_hostname].token | default('undefined') }}" when: (device_info is defined) and (device_info[inventory_hostname] is defined) and (device_info[inventory_hostname].token is defined) ...

 

Satoshi_Toyosa1
F5 Employee
F5 Employee

1) The maximum number of tokens per user is set to 100 since BIG-IP 13.1. The response JSON body to a token request indicates: "user foo has reached maximum active login tokens" (seems like the response body you pasted does not contain this message, so you may be hitting some other issues).

 

2) To find the active tokens on the box, call a GET request to /mgmt/shared/authz/tokens. Use admin user.

 

3) The lifespan of token is 1200s (20 min) by default. You can change it by PATCHING the timeout property of the token: e.g. To change the lifetime of the token "AEDEM4TRWHGBET2TWOHM6ZBJKD" to 4200s;

curl -sk https://$HOST/mgmt/shared/authz/tokens/AEDEM4TRWHGBET2TWOHM6ZBJKD \ -X PATCH -H "Content-type: application/json" \ -H "X-F5-Auth-Token: AEDEM4TRWHGBET2TWOHM6ZBJKD" \ -d '{"timeout" : 4200}'

Note that the Authentication token is designed to be reused. If you are creating a token for each individual task, you may need to consolidate the tasks into one session and request just one token at the beginning of the session. If that's too tedious, you may want to make the timeout shorter.

 

4) You can delete the token by sending a DELETE method: e.g.,

curl -sku $PASS https://$HOST/mgmt/shared/authz/tokens/2PBX7ROP6H4GE6TQN4CUJVJYZG -X DELETE

Cheers

 

thanks for you detail reply . look like login failure at my side is not caused by token limit , do you have any idea why login failure ,since I was able to login before . thanks

Hi Satoshi San,

thanks for the answer and details. If I remember right I tested the ability to extend the tokens lifetime in the past and it didnt work as expected. I will check it again and reply in this thread.

I will add the deletion of the token to my Ansible tasks in case a token is about to expire.

(Especially in a device onboarding workflow a time of 20 minutes can be exceeded easily. That´s why I re-run the tasks above at critical points.)

As I´m re-using the token continuosly, there is little to no risk to exceed the per device limit. But for a clean deployment it makes sense to delete tokens in advance.

Thanks again & kind regards, Stephan

Satoshi_Toyosa1
F5 Employee
F5 Employee

It is not obvious from the error message you provided, however, a usual error message you get from authorization error (e.g., incorrect password) is "message": "Authentication failed." The issue may be a bit deeper than you may think. Try restarting the iControl REST framework daemon by running 'tmsh restart sys service restjavad'. If the issue still persists, I recommend you to file a service ticket to F5 support.

thanks for your reply.

I have contact F5 support , it turn out the process on F5 , after restart javarestd , it recover

Satoshi_Toyosa1
F5 Employee
F5 Employee

Perhaps you just want to remove the token right after running the API operation? 

The script below is using an auth token to patch a sample configuration and afterwards the token will be deleted automatically:

# python script: apitest2.py
# version: 0.2 (2022--05-09)
# author: Stephan Manthey
# purpose:
#   retrieve auth token
#   modify configuration (enable/disable pool member) with token based auth
#   delete auth token
# module requests required (installed via Python PIP):
#   su -c 'yum install python-pip'
#   su -c 'sudo pip2 install requests'
#   su -c 'sudo pip3 install requests'
# or:
#   su -c 'yum install python-requests'
#   su -c 'yum install python3-requests'

import time
import json
import requests
from requests.packages.urllib3.exceptions import InsecureRequestWarning
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)

username = 'username'
password = 'password'
bigipdev = '10.100.100.81'
poolname = 'pool_apitest'
nodename = '10.10.10.11'
nodeport = 80

authpath = 'https://{}/mgmt/shared/authn/login'.format(bigipdev)
conthead = {'Content-Type': 'application/json'}
authdata = {'username': username, 'password': password, 'loginProviderName': 'tmos'}
memberup = {'state': 'user-up', 'session': 'user-enabled'}
memberdown = {'state': 'user-down', 'session': 'user-disabled'}

session = requests.Session()

authtime = time.time()
tokenrequest = session.post(url=authpath,data=json.dumps(authdata),headers=conthead,verify=False)
print('got my token:','{:f}'.format(time.time() - authtime))
# print('token request:',tokenrequest.status_code)
if tokenrequest.status_code == 200:
  tokendata = tokenrequest.json()
  xauthhead = {'X-F5-Auth-Token': tokendata['token']['token'], 'Content-Type': 'application/json'}
  querypath = 'https://{}/mgmt/tm/ltm/pool/~Common~{}/members/~Common~{}:{}'.format(bigipdev,poolname,nodename,nodeport)
  print('patching now:','{:f}'.format(time.time() - authtime))
  membermodify = session.patch(url=querypath,data=json.dumps(memberup),headers=xauthhead,verify=False)
  print('1st response:','{:f}'.format(time.time() - authtime))
  if membermodify.status_code == 200:
    memberdata = membermodify.json()
    membername = memberdata['name']
    # print('member found:',membername)
  else:
    print('modification error:',membermodify.status_code)
    exit()
  membermodify = session.patch(url=querypath,data=json.dumps(memberdown),headers=xauthhead,verify=False)
  print('2nd response:','{:f}'.format(time.time() - authtime))
  if membermodify.status_code == 200:
    memberdata = membermodify.json()
    membername = memberdata['name']
    # print('member found:',membername)
  else:
    print('modification error:',membermodify.status_code)
    exit()
  tokendelpath = 'https://{}/mgmt/shared/authz/tokens/{}'.format(bigipdev,tokendata['token']['token'])
  tokendelete = session.delete(url=tokendelpath,headers=xauthhead,verify=False)
  # print('token delete:', tokendelete.status_code)
  if tokendelete.status_code != 200:
    print('token delete error')
    exit()
else:
  print('error: no token provided')
  exit()

PS: Probably you also want to add some lines to save into the startup configuration