Coverage for gws-app/gws/plugin/auth_method/basic/__init__.py: 100%
46 statements
« prev ^ index » next coverage.py v7.17.0, created at 2026-10-23 20:06 +0200
« prev ^ index » next coverage.py v7.17.0, created at 2026-10-23 20:06 +0200
1"""HTTP Basic authentication method.
3The client sends the credentials with every request in an
4``Authorization: Basic ...`` header. The method decodes the header into
5``username`` and ``password`` credentials, passes them to the authentication
6providers and opens a transient session for the authenticated user. No cookie
7is set.
9The method is registered as a middleware after ``auth``. If a GET request is
10denied with status ``403``, the response is changed to ``401`` with a
11``WWW-Authenticate`` header, so that browsers show a login dialog.
13Example::
15 auth.methods+ {
16 type "basic"
17 realm "My Application"
18 }
19"""
21from typing import Optional
23import gws
24import gws.base.auth
25import gws.base.web
26import gws.lib.text
29@gws.ext.config.authMethod('basic')
30class Config(gws.base.auth.method.Config):
31 """HTTP basic authentication."""
33 realm: Optional[str]
34 """Authentication realm sent to the client."""
37@gws.ext.object.authMethod('basic')
38class Object(gws.base.auth.method.Object):
39 """HTTP Basic authentication method."""
41 realm: str
42 """Authentication realm sent in the ``WWW-Authenticate`` header."""
44 def configure(self):
45 self.uid = 'gws.plugin.auth_method.basic'
46 self.realm = self.cfg('realm', default='Restricted Area')
47 self.root.app.middlewareMgr.register(self, self.uid, depends_on=['auth'])
49 ##
51 def exit_middleware(self, req, res):
52 if res.status == 403 and req.isGet:
53 res.set_status(401)
54 res.add_header('WWW-Authenticate', f'Basic realm={self.realm}, charset="UTF-8"')
55 gws.log.debug(f'auth basic: redirect {res.status=}')
58 def open_session(self, req):
59 am = self.root.app.authMgr
60 credentials = self._parse_header(req)
61 if not credentials:
62 return
63 user = am.authenticate(self, credentials, req)
64 if user:
65 return am.create_transient_session(self, user)
67 def _parse_header(self, req: gws.WebRequester):
68 """Extract the username and password from the ``Authorization`` header."""
69 h = req.header('Authorization')
70 if not h:
71 return
73 a = h.strip().split()
74 if len(a) != 2 or a[0].lower() != 'basic':
75 return
77 try:
78 b = gws.u.to_str(gws.lib.text.from_base64(a[1]))
79 except gws.lib.text.Error:
80 return
82 c = b.split(':', 1)
83 if len(c) != 2:
84 return
86 username = c[0].strip()
87 if not username:
88 return
90 return gws.Data(username=username, password=c[1])