com.here.account.auth.OAuth1Signer.java Source code

Java tutorial

Introduction

Here is the source code for com.here.account.auth.OAuth1Signer.java

Source

/*
 * Copyright (c) 2016 HERE Europe B.V.
 * 
 * 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 com.here.account.auth;

import com.here.account.http.HttpProvider;
import com.here.account.http.HttpProvider.HttpRequest;
import com.here.account.util.Clock;
import org.apache.commons.codec.binary.Base64;

import java.util.List;
import java.util.Map;
import java.util.concurrent.ThreadLocalRandom;

/**
 * Appends the 
 * <a href="https://tools.ietf.org/html/rfc5849">The OAuth 1.0 Protocol</a> 
 * signature to the HTTP request.
 * This request signer computes the signature components of the 
 * "OAuth" auth-scheme and adds them as the Authorization header value.
 * 
 * <p>
 * See also 
 * <a href="http://www.iana.org/assignments/http-authschemes/http-authschemes.xhtml#authschemes">
 * HTTP Authentication Scheme Registry</a> for a list of authschemes.
 * 
 * @author kmccrack
 */
public class OAuth1Signer implements HttpProvider.HttpRequestAuthorizer {

    /**
     * HERE Account recommends 6-character nonces.
     */
    private static final int NONCE_LENGTH = 6;

    private final Clock clock;

    /**
     * HERE client accessKeyId.  Becomes the value of oauth_consumer_key in the 
     * Authorization: OAuth header.
     */
    private final String accessKeyId;

    /**
     * HERE client accessKeySecret.  Used to calculate the oauth_signature in the 
     * Authorization: OAuth header.
     */
    private final String accessKeySecret;

    /**
     * Construct the OAuth signer based on accessKeyId and accessKeySecret.
     * 
     * @param accessKeyId the HERE client accessKeyId.  Becomes the value of oauth_consumer_key in 
     *      the Authorization: OAuth header.
     * @param accessKeySecret the HERE client accessKeySecret.  Used to calculate the oauth_signature 
     *      in the Authorization: OAuth header.
     */
    public OAuth1Signer(String accessKeyId, String accessKeySecret) {
        this(Clock.SYSTEM, accessKeyId, accessKeySecret);
    }

    /**
     * Construct the OAuth signer based on clock, clientId, and clientSecret.
     * Use this if you want to inject your own clock, such as during unit tests.
     * 
     * @param clock the implementation of a clock you want to use
     * @param accessKeyId the HERE clientId.  Becomes the value of oauth_consumer_key in 
     *      the Authorization: OAuth header.
     * @param accessKeySecret the HERE clientSecret.  Used to calculate the oauth_signature 
     *      in the Authorization: OAuth header.
     */
    OAuth1Signer(Clock clock, String accessKeyId, String accessKeySecret) {
        this.clock = clock;
        this.accessKeyId = accessKeyId;
        this.accessKeySecret = accessKeySecret;
    }

    /**
     * The source of entropy for OAuth1.0 nonce values.
     * File bytes with entropy for OAuth1.0 nonce values.
     * Note the OAuth1.0 spec specifically tells us we do not need to use a SecureRandom 
     * number generator.
     * 
     * @param bytes the byte array in which to stick the nonce value
     */
    protected void nextBytes(byte[] bytes) {
        ThreadLocalRandom.current().nextBytes(bytes);
        ;
    }

    /**
     * For cases where there is no Content-Type: application/x-www-form-urlencoded, 
     * and no request token, call this method to get the Authorization Header Value 
     * for a single request.  
     * 
     * <p>
     * Computes the OAuth1 Authorization header value including all required components of the 
     * OAuth type.
     * See also the OAuth 1.0
     * <a href="https://tools.ietf.org/html/rfc5849#section-3.5.1">Authorization Header</a>
     * Section.
     * 
     * <p>
     * Note that the client accessKeySecret, once configured on this object, does not leave this method, 
     * as signatures are used in its place on the wire.
     * 
     * @param method
     * @return
     */
    private String getAuthorizationHeaderValue(String method, String url, Map<String, List<String>> formParams) {
        SignatureCalculator calculator = getSignatureCalculator();

        // <a href="https://tools.ietf.org/html/rfc5849#section-3.3">timestamp</no I a>.
        // the number of seconds since January 1, 1970 00:00:00 GMT
        long timestamp = clock.currentTimeMillis() / 1000L;
        // choose the first 6 chars from base64 alphabet
        byte[] bytes = new byte[NONCE_LENGTH];
        nextBytes(bytes);
        String nonce = Base64.encodeBase64URLSafeString(bytes).substring(0, NONCE_LENGTH);
        String computedSignature = calculator.calculateSignature(method, url, timestamp, nonce,
                SignatureMethod.HMACSHA256, formParams, null);

        return calculator.constructAuthHeader(computedSignature, nonce, timestamp, SignatureMethod.HMACSHA256);
    }

    /**
     * Gets the signature calculator, given that we don't use a user auth, and we do use 
     * the configured client accessKeyId, client accessKeySecret pair.
     * 
     * @return
     */
    SignatureCalculator getSignatureCalculator() {
        // client accessKeyId is "Client Identifier" a.k.a. "oauth_consumer_key" in the OAuth1.0 spec
        // client accessKeySecret is "Client Shared-Secret" , which becomes the client shared-secret component 
        // of the HMAC-SHA1 key per http://tools.ietf.org/html/rfc5849#section-3.4.2.
        SignatureCalculator calculator = new SignatureCalculator(accessKeyId, accessKeySecret);
        return calculator;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void authorize(HttpRequest httpRequest, String method, String url,
            Map<String, List<String>> formParams) {
        String authorizationHeaderValue = getAuthorizationHeaderValue(method, url, formParams);
        httpRequest.addAuthorizationHeader(authorizationHeaderValue);
    }

}