1 module hunt.framework.auth.JwtAuthRealm;
2 
3 import hunt.framework.auth.AuthRealm;
4 import hunt.framework.auth.Claim;
5 import hunt.framework.auth.ClaimTypes;
6 import hunt.framework.auth.Identity;
7 import hunt.framework.auth.JwtToken;
8 import hunt.framework.auth.JwtUtil;
9 import hunt.framework.auth.principal;
10 import hunt.framework.auth.UserDetails;
11 import hunt.framework.auth.UserService;
12 import hunt.framework.config.AuthUserConfig;
13 import hunt.framework.Init;
14 import hunt.framework.provider.ServiceProvider;
15 
16 
17 import hunt.collection.ArrayList;
18 import hunt.collection.Collection;
19 import hunt.http.AuthenticationScheme;
20 import hunt.logging;
21 import hunt.shiro;
22 import hunt.String;
23 
24 import std.algorithm;
25 import std.range;
26 import std.string;
27 
28 
29 /**
30  * See_also:
31  *  [Springboot Integrate with Apache Shiro](http://www.andrew-programming.com/2019/01/23/springboot-integrate-with-jwt-and-apache-shiro/)
32  *  https://stackoverflow.com/questions/13686246/shiro-security-multiple-realms-which-authorization-info-is-taken
33  */
34 class JwtAuthRealm : AuthRealm {
35 
36     this(UserService userService) {
37         super(userService);
38     }
39 
40     override bool supports(AuthenticationToken token) {
41         version(HUNT_AUTH_DEBUG) tracef("AuthenticationToken: %s", typeid(cast(Object)token));
42         
43         JwtToken t = cast(JwtToken)token;
44         return t !is null;
45     }
46 
47     // override protected UserService getUserService() {
48     //     return serviceContainer().resolve!UserService();
49     // }
50 
51     override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) {
52         string tokenString = token.getPrincipal();
53         string username = JwtUtil.getUsername(tokenString);
54         if (username.empty) {
55             version(HUNT_DEBUG) warning("The username in token is empty.");
56             throw new AuthenticationException("token invalid");
57         }
58 
59         UserService userService = getUserService();
60         version(HUNT_AUTH_DEBUG) {
61             infof("username: %s, %s", username, typeid(cast(Object)userService));
62         }        
63 
64         // To retrieve the user info from username
65         UserDetails user = userService.getByName(username);
66         if(user is null) {
67             throw new AuthenticationException(format("The user [%s] does NOT exist!", username));
68         }
69 
70         if(!user.isEnabled)
71             throw new AuthenticationException("The user is disabled!");
72 
73         string salt = user.salt; // userService.getSalt(username, "user.password");
74         version(HUNT_AUTH_DEBUG) {
75             infof("tokenString: %s,  username: %s, salt: %s", tokenString, username, salt);
76         }      
77 
78         // Valid the user using JWT
79         if(!JwtUtil.verify(tokenString, username, salt)) {
80             throw new IncorrectCredentialsException("Wrong username or password for " ~ username);
81         }
82 
83         version(HUNT_AUTH_DEBUG) infof("Realm: %s", getName());
84         PrincipalCollection pCollection = new SimplePrincipalCollection(user, getName());
85         String credentials = new String(tokenString);
86 
87         SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(pCollection, credentials);
88         return info;
89     }
90 
91 }