2 * Copyright (c) 2010-2023 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 Optional<Server> server = Optional.empty();
50 private AccessTokenRefreshListener listener;
51 private AccountConfiguration config;
52 private OAuthClientService oacs;
53 private String callbackUrl;
55 public CallbackServer(AccessTokenRefreshListener l, HttpClient hc, OAuthFactory oAuthFactory,
56 AccountConfiguration config, String callbackUrl) {
57 oacs = oAuthFactory.createOAuthClientService(config.clientId, Constants.MB_TOKEN_URL, Constants.MB_AUTH_URL,
58 config.clientId, config.clientSecret, config.getScope(), false);
60 AUTH_MAP.put(Integer.valueOf(config.callbackPort), oacs);
61 SERVER_MAP.put(Integer.valueOf(config.callbackPort), this);
63 this.callbackUrl = callbackUrl;
64 INVALID_ACCESS_TOKEN.setAccessToken(Constants.EMPTY);
67 public String getAuthorizationUrl() {
69 return oacs.getAuthorizationUrl(callbackUrl, null, null);
70 } catch (OAuthException e) {
71 LOGGER.warn("Error creating Authorization URL {}", e.getMessage());
72 return Constants.EMPTY;
76 public String getScope() {
77 return config.getScope();
80 public boolean start() {
81 LOGGER.debug("Start Callback Server for port {}", config.callbackPort);
82 if (!server.isEmpty()) {
83 LOGGER.debug("Callback server for port {} already started", config.callbackPort);
86 server = Optional.of(new Server());
87 ServerConnector connector = new ServerConnector(server.get());
88 connector.setPort(config.callbackPort);
89 server.get().setConnectors(new Connector[] { connector });
90 ServletHandler servletHandler = new ServletHandler();
91 server.get().setHandler(servletHandler);
92 servletHandler.addServletWithMapping(CallbackServlet.class, Constants.CALLBACK_ENDPOINT);
95 } catch (Exception e) {
96 LOGGER.warn("Cannot start Callback Server for port {}, Error {}", config.callbackPort, e.getMessage());
103 LOGGER.debug("Stop Callback Server");
105 if (!server.isEmpty()) {
107 server = Optional.empty();
109 } catch (Exception e) {
110 LOGGER.warn("Cannot start Callback Server for port {}, Error {}", config.callbackPort, e.getMessage());
114 public String getToken() {
115 AccessTokenResponse atr = null;
118 * this will automatically trigger
119 * - return last stored token if it's still valid
120 * - refreshToken if current token is expired
121 * - inform listeners if refresh delivered new token
122 * - store new token in persistence
124 atr = oacs.getAccessTokenResponse();
125 } catch (OAuthException | IOException | OAuthResponseException e) {
126 LOGGER.warn("Exception getting token {}", e.getMessage());
129 LOGGER.debug("Token empty - Manual Authorization needed at {}", callbackUrl);
130 listener.onAccessTokenResponse(INVALID_ACCESS_TOKEN);
131 return INVALID_ACCESS_TOKEN.getAccessToken();
133 listener.onAccessTokenResponse(atr);
134 return atr.getAccessToken();
138 * Static callback for Servlet calls
143 public static void callback(int port, String code) {
144 LOGGER.trace("Callback from Servlet {} {}", port, code);
146 OAuthClientService oacs = AUTH_MAP.get(port);
147 LOGGER.trace("Get token from code {}", code);
148 // get CallbackServer instance
149 CallbackServer srv = SERVER_MAP.get(port);
150 LOGGER.trace("Deliver token to {}", srv);
151 if (srv != null && oacs != null) {
152 // token stored and persisted inside oacs
153 AccessTokenResponse atr = oacs.getAccessTokenResponseByAuthorizationCode(code, srv.callbackUrl);
154 // inform listener - not done by oacs
155 srv.listener.onAccessTokenResponse(atr);
157 LOGGER.warn("Either Callbackserver {} or Authorization Service {} not found", srv, oacs);
159 } catch (OAuthException | IOException | OAuthResponseException e) {
160 LOGGER.warn("Exception getting token from code {} {}", code, e.getMessage());
164 public static String getAuthorizationUrl(int port) {
165 CallbackServer srv = SERVER_MAP.get(port);
167 return srv.getAuthorizationUrl();
169 LOGGER.debug("No Callbackserver found for {}", port);
170 return Constants.EMPTY;
174 public static String getScope(int port) {
175 CallbackServer srv = SERVER_MAP.get(port);
177 return srv.getScope();
179 LOGGER.debug("No Callbackserver found for {}", port);
180 return Constants.EMPTY;