diff --git a/src/services/api/graphql/graphql/auth_token_mutation.py b/src/services/api/graphql/graphql/auth_token_mutation.py
index 21ac40094..603a13758 100644
--- a/src/services/api/graphql/graphql/auth_token_mutation.py
+++ b/src/services/api/graphql/graphql/auth_token_mutation.py
@@ -1,49 +1,61 @@
 # Copyright 2022 VyOS maintainers and contributors <maintainers@vyos.io>
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
 # License as published by the Free Software Foundation; either
 # version 2.1 of the License, or (at your option) any later version.
 #
 # This library is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 # Lesser General Public License for more details.
 #
 # You should have received a copy of the GNU Lesser General Public License
 # along with this library.  If not, see <http://www.gnu.org/licenses/>.
 
 import jwt
 import datetime
 from typing import Any, Dict
 from ariadne import ObjectType, UnionType
 from graphql import GraphQLResolveInfo
 
 from .. libs.token_auth import generate_token
+from .. session.session import get_user_info
 from .. import state
 
 auth_token_mutation = ObjectType("Mutation")
 
 @auth_token_mutation.field('AuthToken')
 def auth_token_resolver(obj: Any, info: GraphQLResolveInfo, data: Dict):
     # non-nullable fields
     user = data['username']
     passwd = data['password']
 
     secret = state.settings['secret']
     exp_interval = int(state.settings['app'].state.vyos_token_exp)
     expiration = (datetime.datetime.now(tz=datetime.timezone.utc) +
                   datetime.timedelta(seconds=exp_interval))
 
     res = generate_token(user, passwd, secret, expiration)
-    if res:
+    try:
+        res |= get_user_info(user)
+    except ValueError:
+        # non-existent user already caught
+        pass
+    if 'token' in res:
         data['result'] = res
         return {
             "success": True,
             "data": data
         }
 
+    if 'errors' in res:
+        return {
+            "success": False,
+            "errors": res['errors']
+        }
+
     return {
         "success": False,
         "errors": ['token generation failed']
     }
diff --git a/src/services/api/graphql/libs/token_auth.py b/src/services/api/graphql/libs/token_auth.py
index 2100eba7f..8585485c9 100644
--- a/src/services/api/graphql/libs/token_auth.py
+++ b/src/services/api/graphql/libs/token_auth.py
@@ -1,71 +1,70 @@
 import jwt
 import uuid
 import pam
 from secrets import token_hex
 
 from .. import state
 
 def _check_passwd_pam(username: str, passwd: str) -> bool:
     if pam.authenticate(username, passwd):
         return True
     return False
 
 def init_secret():
     length = int(state.settings['app'].state.vyos_secret_len)
     secret = token_hex(length)
     state.settings['secret'] = secret
 
 def generate_token(user: str, passwd: str, secret: str, exp: int) -> dict:
     if user is None or passwd is None:
         return {}
     if _check_passwd_pam(user, passwd):
         app = state.settings['app']
         try:
             users = app.state.vyos_token_users
         except AttributeError:
             app.state.vyos_token_users = {}
             users = app.state.vyos_token_users
         user_id = uuid.uuid1().hex
         payload_data = {'iss': user, 'sub': user_id, 'exp': exp}
         secret = state.settings.get('secret')
         if secret is None:
-            return {
-                    "success": False,
-                    "errors": ['failed secret generation']
-                   }
+            return {"errors": ['missing secret']}
         token = jwt.encode(payload=payload_data, key=secret, algorithm="HS256")
 
         users |= {user_id: user}
         return {'token': token}
+    else:
+        return {"errors": ['failed pam authentication']}
 
 def get_user_context(request):
     context = {}
     context['request'] = request
     context['user'] = None
     if 'Authorization' in request.headers:
         auth = request.headers['Authorization']
         scheme, token = auth.split()
         if scheme.lower() != 'bearer':
             return context
 
         try:
             secret = state.settings.get('secret')
             payload = jwt.decode(token, secret, algorithms=["HS256"])
             user_id: str = payload.get('sub')
             if user_id is None:
                 return context
         except jwt.exceptions.ExpiredSignatureError:
             context['error'] = 'expired token'
             return context
         except jwt.PyJWTError:
             return context
         try:
             users = state.settings['app'].state.vyos_token_users
         except AttributeError:
             return context
 
         user = users.get(user_id)
         if user is not None:
             context['user'] = user
 
     return context
diff --git a/src/services/api/graphql/session/session.py b/src/services/api/graphql/session/session.py
index b2aef9bd9..3c5a062b6 100644
--- a/src/services/api/graphql/session/session.py
+++ b/src/services/api/graphql/session/session.py
@@ -1,204 +1,212 @@
 # Copyright 2021-2022 VyOS maintainers and contributors <maintainers@vyos.io>
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
 # License as published by the Free Software Foundation; either
 # version 2.1 of the License, or (at your option) any later version.
 #
 # This library is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 # Lesser General Public License for more details.
 #
 # You should have received a copy of the GNU Lesser General Public License
 # along with this library.  If not, see <http://www.gnu.org/licenses/>.
 
 import os
 import json
 
 from ariadne import convert_camel_case_to_snake
 
 from vyos.config import Config
 from vyos.configtree import ConfigTree
 from vyos.defaults import directories
 from vyos.template import render
 from vyos.opmode import Error as OpModeError
 
 from api.graphql.libs.op_mode import load_op_mode_as_module, split_compound_op_mode_name
 from api.graphql.libs.op_mode import normalize_output
 
 op_mode_include_file = os.path.join(directories['data'], 'op-mode-standardized.json')
 
+def get_config_dict(path=[], effective=False, key_mangling=None,
+                     get_first_key=False, no_multi_convert=False,
+                     no_tag_node_value_mangle=False):
+    config = Config()
+    return config.get_config_dict(path=path, effective=effective,
+                                  key_mangling=key_mangling,
+                                  get_first_key=get_first_key,
+                                  no_multi_convert=no_multi_convert,
+                                  no_tag_node_value_mangle=no_tag_node_value_mangle)
+
+def get_user_info(user):
+    user_info = {}
+    info = get_config_dict(['system', 'login', 'user', user],
+                           get_first_key=True)
+    if not info:
+        raise ValueError("No such user")
+
+    user_info['user'] = user
+    user_info['full_name'] = info.get('full-name', '')
+
+    return user_info
+
 class Session:
     """
     Wrapper for calling configsession functions based on GraphQL requests.
     Non-nullable fields in the respective schema allow avoiding a key check
     in 'data'.
     """
     def __init__(self, session, data):
         self._session = session
         self._data = data
         self._name = convert_camel_case_to_snake(type(self).__name__)
 
         try:
             with open(op_mode_include_file) as f:
                 self._op_mode_list = json.loads(f.read())
         except Exception:
             self._op_mode_list = None
 
-    @staticmethod
-    def _get_config_dict(path=[], effective=False, key_mangling=None,
-                         get_first_key=False, no_multi_convert=False,
-                         no_tag_node_value_mangle=False):
-        config = Config()
-        return config.get_config_dict(path=path, effective=effective,
-                                      key_mangling=key_mangling,
-                                      get_first_key=get_first_key,
-                                      no_multi_convert=no_multi_convert,
-                                      no_tag_node_value_mangle=no_tag_node_value_mangle)
-
     def show_config(self):
         session = self._session
         data = self._data
         out = ''
 
         try:
             out = session.show_config(data['path'])
             if data.get('config_format', '') == 'json':
                 config_tree = ConfigTree(out)
                 out = json.loads(config_tree.to_json())
         except Exception as error:
             raise error
 
         return out
 
     def save_config_file(self):
         session = self._session
         data = self._data
         if 'file_name' not in data or not data['file_name']:
             data['file_name'] = '/config/config.boot'
 
         try:
             session.save_config(data['file_name'])
         except Exception as error:
             raise error
 
     def load_config_file(self):
         session = self._session
         data = self._data
 
         try:
             session.load_config(data['file_name'])
             session.commit()
         except Exception as error:
             raise error
 
     def show(self):
         session = self._session
         data = self._data
         out = ''
 
         try:
             out = session.show(data['path'])
         except Exception as error:
             raise error
 
         return out
 
     def add_system_image(self):
         session = self._session
         data = self._data
 
         try:
             res = session.install_image(data['location'])
         except Exception as error:
             raise error
 
         return res
 
     def delete_system_image(self):
         session = self._session
         data = self._data
 
         try:
             res = session.remove_image(data['name'])
         except Exception as error:
             raise error
 
         return res
 
     def show_user_info(self):
         session = self._session
         data = self._data
 
         user_info = {}
         user = data['user']
         try:
-            info = self._get_config_dict(['system', 'login', 'user', user,
-                                          'full-name'])
-            user_info['user'] = user
-            user_info['full_name'] = info.get('full-name', '')
+            user_info = get_user_info(user)
         except Exception as error:
             raise error
 
         return user_info
 
     def system_status(self):
         import api.graphql.session.composite.system_status as system_status
 
         session = self._session
         data = self._data
 
         status = {}
         status['host_name'] = session.show(['host', 'name']).strip()
         status['version'] = system_status.get_system_version()
         status['uptime'] = system_status.get_system_uptime()
         status['ram'] = system_status.get_system_ram_usage()
 
         return status
 
     def gen_op_query(self):
         session = self._session
         data = self._data
         name = self._name
         op_mode_list = self._op_mode_list
 
         # handle the case that the op-mode file contains underscores:
         if op_mode_list is None:
             raise FileNotFoundError(f"No op-mode file list at '{op_mode_include_file}'")
         (func_name, scriptname) = split_compound_op_mode_name(name, op_mode_list)
         if scriptname == '':
             raise FileNotFoundError(f"No op-mode file named in string '{name}'")
 
         mod = load_op_mode_as_module(f'{scriptname}')
         func = getattr(mod, func_name)
         try:
             res = func(True, **data)
         except OpModeError as e:
             raise e
 
         res = normalize_output(res)
 
         return res
 
     def gen_op_mutation(self):
         session = self._session
         data = self._data
         name = self._name
         op_mode_list = self._op_mode_list
 
         # handle the case that the op-mode file name contains underscores:
         if op_mode_list is None:
             raise FileNotFoundError(f"No op-mode file list at '{op_mode_include_file}'")
         (func_name, scriptname) = split_compound_op_mode_name(name, op_mode_list)
         if scriptname == '':
             raise FileNotFoundError(f"No op-mode file named in string '{name}'")
 
         mod = load_op_mode_as_module(f'{scriptname}')
         func = getattr(mod, func_name)
         try:
             res = func(**data)
         except OpModeError as e:
             raise e
 
         return res