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

1"""HTTP Basic authentication method. 

2 

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. 

8 

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. 

12 

13Example:: 

14 

15 auth.methods+ { 

16 type "basic" 

17 realm "My Application" 

18 } 

19""" 

20 

21from typing import Optional 

22 

23import gws 

24import gws.base.auth 

25import gws.base.web 

26import gws.lib.text 

27 

28 

29@gws.ext.config.authMethod('basic') 

30class Config(gws.base.auth.method.Config): 

31 """HTTP basic authentication.""" 

32 

33 realm: Optional[str] 

34 """Authentication realm sent to the client.""" 

35 

36 

37@gws.ext.object.authMethod('basic') 

38class Object(gws.base.auth.method.Object): 

39 """HTTP Basic authentication method.""" 

40 

41 realm: str 

42 """Authentication realm sent in the ``WWW-Authenticate`` header.""" 

43 

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']) 

48 

49 ## 

50 

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=}') 

56 

57 

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) 

66 

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 

72 

73 a = h.strip().split() 

74 if len(a) != 2 or a[0].lower() != 'basic': 

75 return 

76 

77 try: 

78 b = gws.u.to_str(gws.lib.text.from_base64(a[1])) 

79 except gws.lib.text.Error: 

80 return 

81 

82 c = b.split(':', 1) 

83 if len(c) != 2: 

84 return 

85 

86 username = c[0].strip() 

87 if not username: 

88 return 

89 

90 return gws.Data(username=username, password=c[1])