2 * Copyright (c) 2010-2024 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
7 * This program and the accompanying materials are made available under the
8 * terms of the Eclipse Public License 2.0 which is available at
9 * http://www.eclipse.org/legal/epl-2.0
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.mercedesme.internal.server;
15 import java.io.IOException;
16 import java.util.HashMap;
18 import java.util.Optional;
20 import org.eclipse.jdt.annotation.NonNullByDefault;
21 import org.eclipse.jetty.client.HttpClient;
22 import org.eclipse.jetty.server.Connector;
23 import org.eclipse.jetty.server.Server;
24 import org.eclipse.jetty.server.ServerConnector;
25 import org.eclipse.jetty.servlet.ServletHandler;
26 import org.openhab.binding.mercedesme.internal.Constants;
27 import org.openhab.binding.mercedesme.internal.config.AccountConfiguration;
28 import org.openhab.core.auth.client.oauth2.AccessTokenRefreshListener;
29 import org.openhab.core.auth.client.oauth2.AccessTokenResponse;
30 import org.openhab.core.auth.client.oauth2.OAuthClientService;
31 import org.openhab.core.auth.client.oauth2.OAuthException;
32 import org.openhab.core.auth.client.oauth2.OAuthFactory;
33 import org.openhab.core.auth.client.oauth2.OAuthResponseException;
34 import org.slf4j.Logger;
35 import org.slf4j.LoggerFactory;
38 * The {@link CallbackServer} class defines an HTTP Server for authentication callbacks
40 * @author Bernd Weymann - Initial contribution
43 public class CallbackServer {
44 private static final Logger LOGGER = LoggerFactory.getLogger(CallbackServer.class);
45 private static final Map<Integer, OAuthClientService> AUTH_MAP = new HashMap<Integer, OAuthClientService>();
46 private static final Map<Integer, CallbackServer> SERVER_MAP = new HashMap<Integer, CallbackServer>();
47 private static final AccessTokenResponse INVALID_ACCESS_TOKEN = new AccessTokenResponse();
49 private final OAuthFactory oAuthFactory;
51 private Optional<Server> server = Optional.empty();
52 private AccessTokenRefreshListener listener;
53 private AccountConfiguration config;
54 private OAuthClientService oacs;
55 private String callbackUrl;
57 public CallbackServer(AccessTokenRefreshListener l, HttpClient hc, OAuthFactory oAuthFactory,
58 AccountConfiguration config, String callbackUrl) {
59 this.oAuthFactory = oAuthFactory;
60 oacs = oAuthFactory.createOAuthClientService(config.clientId, Constants.MB_TOKEN_URL, Constants.MB_AUTH_URL,
61 config.clientId, config.clientSecret, config.getScope(), false);
63 AUTH_MAP.put(Integer.valueOf(config.callbackPort), oacs);
64 SERVER_MAP.put(Integer.valueOf(config.callbackPort), this);
66 this.callbackUrl = callbackUrl;
67 INVALID_ACCESS_TOKEN.setAccessToken(Constants.EMPTY);
70 public void dispose() {
71 oAuthFactory.ungetOAuthService(config.clientId);
72 AUTH_MAP.remove(Integer.valueOf(config.callbackPort));
73 SERVER_MAP.remove(Integer.valueOf(config.callbackPort));
76 public void deleteOAuthServiceAndAccessToken() {
77 oAuthFactory.deleteServiceAndAccessToken(config.clientId);
80 public String getAuthorizationUrl() {
82 return oacs.getAuthorizationUrl(callbackUrl, null, null);
83 } catch (OAuthException e) {
84 LOGGER.warn("Error creating Authorization URL {}", e.getMessage());
85 return Constants.EMPTY;
89 public String getScope() {
90 return config.getScope();
93 public boolean start() {
94 LOGGER.debug("Start Callback Server for port {}", config.callbackPort);
95 if (server.isPresent()) {
96 LOGGER.debug("Callback server for port {} already started", config.callbackPort);
99 server = Optional.of(new Server());
100 ServerConnector connector = new ServerConnector(server.get());
101 connector.setPort(config.callbackPort);
102 server.get().setConnectors(new Connector[] { connector });
103 ServletHandler servletHandler = new ServletHandler();
104 server.get().setHandler(servletHandler);
105 servletHandler.addServletWithMapping(CallbackServlet.class, Constants.CALLBACK_ENDPOINT);
107 server.get().start();
108 } catch (Exception e) {
109 LOGGER.warn("Cannot start Callback Server for port {}, Error {}", config.callbackPort, e.getMessage());
116 LOGGER.debug("Stop Callback Server");
118 if (server.isPresent()) {
120 server = Optional.empty();
122 } catch (Exception e) {
123 LOGGER.warn("Cannot start Callback Server for port {}, Error {}", config.callbackPort, e.getMessage());
127 public String getToken() {
128 AccessTokenResponse atr = null;
131 * this will automatically trigger
132 * - return last stored token if it's still valid
133 * - refreshToken if current token is expired
134 * - inform listeners if refresh delivered new token
135 * - store new token in persistence
137 atr = oacs.getAccessTokenResponse();
138 } catch (OAuthException | IOException | OAuthResponseException e) {
139 LOGGER.warn("Exception getting token {}", e.getMessage());
142 LOGGER.debug("Token empty - Manual Authorization needed at {}", callbackUrl);
143 listener.onAccessTokenResponse(INVALID_ACCESS_TOKEN);
144 return INVALID_ACCESS_TOKEN.getAccessToken();
146 listener.onAccessTokenResponse(atr);
147 return atr.getAccessToken();
151 * Static callback for Servlet calls
156 public static void callback(int port, String code) {
157 LOGGER.trace("Callback from Servlet {} {}", port, code);
159 OAuthClientService oacs = AUTH_MAP.get(port);
160 LOGGER.trace("Get token from code {}", code);
161 // get CallbackServer instance
162 CallbackServer srv = SERVER_MAP.get(port);
163 LOGGER.trace("Deliver token to {}", srv);
164 if (srv != null && oacs != null) {
165 // token stored and persisted inside oacs
166 AccessTokenResponse atr = oacs.getAccessTokenResponseByAuthorizationCode(code, srv.callbackUrl);
167 // inform listener - not done by oacs
168 srv.listener.onAccessTokenResponse(atr);
170 LOGGER.warn("Either Callbackserver {} or Authorization Service {} not found", srv, oacs);
172 } catch (OAuthException | IOException | OAuthResponseException e) {
173 LOGGER.warn("Exception getting token from code {} {}", code, e.getMessage());
177 public static String getAuthorizationUrl(int port) {
178 CallbackServer srv = SERVER_MAP.get(port);
180 return srv.getAuthorizationUrl();
182 LOGGER.debug("No Callbackserver found for {}", port);
183 return Constants.EMPTY;
187 public static String getScope(int port) {
188 CallbackServer srv = SERVER_MAP.get(port);
190 return srv.getScope();
192 LOGGER.debug("No Callbackserver found for {}", port);
193 return Constants.EMPTY;