Bearer Token Authentifizierung fuer neon
ulrich
2024-02-20 177043dea9f4efe18cea2ee7864ddb7b25c4646a
commit | author | age
177043 1 /*
U 2   neon-auth - Authentication Extensions to Neon
3   Copyright (C) 2024  Ulrich Hilger
4
5   This program is free software: you can redistribute it and/or modify
6   it under the terms of the GNU Affero General Public License as
7   published by the Free Software Foundation, either version 3 of the
8   License, or (at your option) any later version.
9
10   This program is distributed in the hope that it will be useful,
11   but WITHOUT ANY WARRANTY; without even the implied warranty of
12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   GNU Affero General Public License for more details.
14
15   You should have received a copy of the GNU Affero General Public License
16   along with this program.  If not, see <https://www.gnu.org/licenses/>.
17  */
18 package de.uhilger.neon.auth;
19
20 import com.google.gson.Gson;
21 import com.sun.net.httpserver.HttpContext;
22 import com.sun.net.httpserver.HttpExchange;
23 import de.uhilger.neon.HttpHelper;
24 import java.io.IOException;
25 import java.util.Date;
26 import java.util.Map;
27
28 /**
29  * Objekte der Klasse BearerLoginService erlauben eine Benutzeranmeldung 
30  * mit Hilfe eines BearerAuthenticators, der im HttpContext des 
31  * HttpExchange erwartet wird
32  * 
33  * @author Ulrich Hilger
34  */
35 public class BearerLoginService extends BearerService {
36   
37   private final Directory directory;
38   private long lastLoginTime = 0;
39   
40     
41   public BearerLoginService(Directory directory) {
42     this.directory = directory;
43   }
44   
45   public void login(HttpExchange exchange) throws IOException {
46       User user = getUser(exchange);
47       LoginResponse response = login(exchange, user.getName(), user.getPassword());
48       handleLoginResponse(exchange, response);    
49   }
50   
51   /**
52    * Anmelden
53    * 
54    * @param e das Objekt mit Informationen zu HTTP-Anfrage und -Antwort
55    * @param userId  die Kennung des Benutzers
56    * @param password  das Kennwort des Benutzers
57    * @return Token oder null, wenn die Anmeldung misslang
58    */
59   private LoginResponse login(HttpExchange e, String userId, String password) {
60     HttpContext context = e.getHttpContext();
61     Map attr = context.getAttributes();
62     if(canLogin(attr)) {
63       if (directory.isValid(userId, password)) {
64         LoginResponse r = new LoginResponse();
65         long expireSeconds = Long.parseLong((String) attr.getOrDefault("expireSeconds", "7200"));
66         Object o = context.getAuthenticator();
67         if(o instanceof BearerAuthenticator) {
68           String token = ((BearerAuthenticator) o).createToken(userId, expireSeconds);
69           r.setToken(token);
70           r.setRefreshToken(((BearerAuthenticator) o).createToken(userId, 
71                   Long.parseLong((String) attr.getOrDefault("refreshExpireSeconds", "86400"))));
72           r.setExpiresIn(expireSeconds);
73           return r;
74         } else {
75           return null;
76         }
77       } else {
78         return null;
79       } 
80     } else {
81       return null;
82     }
83   }
84
85   /**
86    * Bei der Deklaration eines BearerAuthenticator in der Serverbeschreibung wird ein 
87    * Attribut z.B. "loginTL": 10000 erwartet. Hier wird geprueft, ob die dort angegebene 
88    * Zahl von Millisekunden verstrichen ist, seit dieser LoginService das letzte Mal 
89    * aufgerufen wurde. Auf diese Weise wird ueber alle Logins, die ueber diesen Service 
90    * ausgefuehrt werden, eine Brute Force Attacke gebremst. Das heisst aber auch, dass 
91    * jeder Login fruehestens nach der Anzahl von Millisekunden seit dem letzten Login 
92    * erfolgen kann.
93    * 
94    * @param attr  die Attribute, in denen das Attribut 'loginTL' erwartet wird
95    * @return true, wenn die konfigurierte Wartezeit verstrichen ist, false, wenn nicht
96    */
97   private boolean canLogin(Map attr) {
98     boolean doLogin = false;
99     long loginTl = Long.parseLong((String) attr.getOrDefault("loginTL", "10000"));
100     if(loginTl > 0 ) {
101       Date now = new Date();
102       long nowTimeMillis = now.getTime();
103       long diff = nowTimeMillis - lastLoginTime;
104       if(loginTl < diff) {
105         lastLoginTime = nowTimeMillis;
106         doLogin = true;
107       }
108     } else {
109       doLogin = true;
110     }
111     return doLogin;
112   }
113   
114   private User getUser(HttpExchange exchange) throws IOException {
115     /*
116     Wenn ein JSON-Inhalt im Body uebermittelt wird, steht
117     dort evtl. etwas wie
118     {"name": "fred", "password": "secret"}
119     das kann wie folgt gelesen werden
120      */
121     String body = new HttpHelper().bodyLesen(exchange);
122     Gson gson = new Gson();
123     User user = gson.fromJson(body, User.class);
124     return user;
125   }
126   
127 }