Java tutorial
/** * Copyright 2018 Nitor Creations Oy * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package io.nitor.api.backend.msgraph; import io.vertx.core.Future; import io.vertx.core.buffer.Buffer; import io.vertx.core.http.HttpClient; import io.vertx.core.http.HttpClientResponse; import io.vertx.core.json.JsonObject; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import static io.netty.handler.codec.http.HttpHeaderNames.ACCEPT; import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_LENGTH; import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_TYPE; import static io.netty.handler.codec.http.HttpHeaderValues.APPLICATION_JSON; import static io.netty.handler.codec.http.HttpHeaderValues.APPLICATION_X_WWW_FORM_URLENCODED; import static io.nitor.api.backend.util.Helpers.urlEncode; import static io.vertx.core.Future.failedFuture; import static io.vertx.core.Future.succeededFuture; import static java.lang.System.currentTimeMillis; import static java.util.concurrent.TimeUnit.SECONDS; public class GraphSessionTokenService { private static final Logger logger = LogManager.getLogger(GraphSessionTokenService.class); private static final TokenData missingToken = new TokenData("<n/a>", -1, null); private final ConcurrentMap<String, TokenData> cache = new ConcurrentHashMap<>(); private final HttpClient httpClient; private final String tokenUrl; private final String baseForm; public GraphSessionTokenService(HttpClient httpClient, JsonObject adConfig) { this.tokenUrl = adConfig.getJsonObject("openIdConfig").getString("token_endpoint"); this.baseForm = "client_id=" + urlEncode(adConfig.getString("clientId")) + "&scope=" + urlEncode(adConfig.getString("scope")) + "&grant_type=refresh_token" + "&client_secret=" + urlEncode(adConfig.getString("clientSecret")) + "&redirect_uri=" + urlEncode(adConfig.getString("redirectUri")); this.httpClient = httpClient; } public Future<TokenData> getAccessToken(String refreshToken) { if (refreshToken == null) { return failedFuture("refresh_token missing from session"); } TokenData d = cache.getOrDefault(refreshToken, missingToken); if (d.expires > currentTimeMillis()) { return succeededFuture(d); } Buffer form = Buffer.buffer(baseForm + "&refresh_token=" + urlEncode(refreshToken)); final Future<TokenData> ret = Future.future(); httpClient.getAbs(tokenUrl).putHeader(ACCEPT, APPLICATION_JSON) .putHeader(CONTENT_TYPE, APPLICATION_X_WWW_FORM_URLENCODED) .putHeader(CONTENT_LENGTH, String.valueOf(form.length())).setTimeout(SECONDS.toMillis(10)) .exceptionHandler(err -> { logger.error("Failed to refresh graph access token", err); ret.fail(err); }).handler(resp -> handleRefreshResponse(resp, ret, d.refreshToken)).end(form); return ret; } private void handleRefreshResponse(HttpClientResponse resp, Future<TokenData> future, String previousRefreshToken) { resp.exceptionHandler(future::fail); resp.bodyHandler(body -> { if (resp.statusCode() != 200) { future.fail(body.toString()); return; } JsonObject json = body.toJsonObject(); TokenData d = new TokenData(json.getString("access_token"), currentTimeMillis() + 1_000 * (json.getLong("expires_in") - 10), json.getString("refresh_token")); cache.put(d.refreshToken, d); if (previousRefreshToken != null) { cache.put(previousRefreshToken, d); } future.complete(d); }); } public static class TokenData { public final String accessToken; public final long expires; public final String refreshToken; public TokenData(String accessToken, long expires, String refreshToken) { this.accessToken = accessToken; this.expires = expires; this.refreshToken = refreshToken; } } }