InitiateLoginService.java

package eu.wuttke.comdirect.login;

import com.fasterxml.jackson.core.JsonProcessingException;
import eu.wuttke.comdirect.util.BaseComdirectService;
import eu.wuttke.comdirect.util.ComdirectException;
import eu.wuttke.comdirect.util.SimpleHttpClient;
import eu.wuttke.comdirect.util.SimpleHttpResponse;

import java.io.IOException;
import java.util.Map;
import java.util.UUID;

public class InitiateLoginService extends BaseComdirectService {

    private static final String X_ONCE_AUTHENTICATION_INFO = "x-once-authentication-info";

    public InitiateLoginService(SimpleHttpClient httpClient) {
        super(httpClient);
    }

    public InitiateLoginResult initiateLogin(LoginCredentials credentials) throws ComdirectException {
        Tokens tokens = postCredentialsForTokens(credentials);
        String sessionId = generateAndGetSessionId(tokens);
        Map<String,String> authInfo = postToValidateSession(tokens, sessionId);

        return new InitiateLoginResult(
                ChallengeType.decodeChallengeType(authInfo.get("typ")),
                authInfo.get("id"),
                authInfo.get("challenge"),
                tokens, sessionId);
    }

    private Map<String, String> postToValidateSession(Tokens tokens, String sessionId) throws ComdirectException {
        try {
            String body = "{\"identifier\":\"" + sessionId + "\"," +
                "\"sessionTanActive\":true," +
                "\"activated2FA\":true}";

            SimpleHttpResponse response = httpClient.postForString(
                    comdirectApiEndpoint + "/api/session/clients/user/v1/sessions/" + sessionId + "/validate",
                    new String[] {
                        ACCEPT_HEADER, APPLICATION_JSON,
                        AUTHORIZATION_HEADER, BEARER + tokens.getAccessToken(),
                        X_HTTP_REQUEST_INFO_HEADER, buildRequestInfoHeader(sessionId),
                        CONTENT_TYPE_HEADER, APPLICATION_JSON
                    },
                    body);

            if (response.getStatusCode() != 201)
                throw new ComdirectException("unable to validate session", response.getStatusCode(), response.getBody());
            if (!response.getHeaders().containsKey(X_ONCE_AUTHENTICATION_INFO))
                throw new ComdirectException("unable to validate session: missing header \"x-once-authentication-info\"", -1, response.getBody());

            String authInfoStr = response.getHeaders().get(X_ONCE_AUTHENTICATION_INFO).get(0);
            return objectMapper.readerFor(Map.class).readValue(authInfoStr);
        } catch (IOException e) {
            throw new ComdirectException("unable to validate session", 0,
                    e.getMessage());
        }
    }

    private String generateAndGetSessionId(Tokens tokens) throws ComdirectException {
        try {
            String sessionId = UUID.randomUUID().toString();

            SimpleHttpResponse response = httpClient.getForString(comdirectApiEndpoint + "/api/session/clients/user/v1/sessions",
                    new String[]{
                            ACCEPT_HEADER, APPLICATION_JSON,
                            AUTHORIZATION_HEADER, BEARER + tokens.getAccessToken(),
                            X_HTTP_REQUEST_INFO_HEADER, buildRequestInfoHeader(sessionId)
                    });

            if (response.getStatusCode() != 200)
                throw new ComdirectException("unable to get session", response.getStatusCode(), response.getBody());

            Map[] sessions = objectMapper.readerFor(Map[].class).readValue(response.getBody());
            return (String) sessions[0].get("identifier");
        } catch (IOException e) {
            throw new ComdirectException("unable to get session", 0,
                    e.getMessage());
        }
    }

    private Tokens postCredentialsForTokens(LoginCredentials credentials) throws ComdirectException {
        try {
            SimpleHttpResponse response = httpClient.postForString(
                    comdirectApiEndpoint + "/oauth/token",
                    new String[]{
                            ACCEPT_HEADER, APPLICATION_JSON,
                            CONTENT_TYPE_HEADER, APPLICATION_X_WWW_FORM_URLENCODED
                    },
                    "client_id=" + credentials.getClientId() +
                            "&client_secret=" + credentials.getClientSecret() +
                            "&username=" + credentials.getUserName() +
                            "&password=" + credentials.getPassword() +
                            "&grant_type=password");

            if (response.getStatusCode() != 200)
                throw new ComdirectException("unable to obtain access token", response.getStatusCode(), response.getBody());

            return parseTokens(response.getBody());
        } catch (IOException e) {
            throw new ComdirectException("unable to obtain access token", 0,
                    e.getMessage());
        }
    }

    private Tokens parseTokens(String body) throws JsonProcessingException {
        Map<String,Object> data = objectMapper.readerFor(Map.class).readValue(body);
        return new Tokens((String)data.get(ACCESS_TOKEN),
                (String)data.get(REFRESH_TOKEN),
                calculateExpiry((Integer)data.get(EXPIRES_IN)));
    }

}