web-dev-qa-db-fra.com

Comment passez-vous l'en-tête d'autorisation via API Gateway au point de terminaison HTTP?

J'ai une API derrière une passerelle AWS API qui doit utiliser l'en-tête Authorization pour le traitement. J'ai malheureusement été incapable de transmettre cela au backend pour traitement.

J'ai essayé de créer l'en-tête de demande HTTP d'autorisation dans ma demande de méthode, puis l'en-tête HTTP d'autorisation correspondant dans ma demande d'intégration (l'autorisation est mappée à partir de method.request.header.Authorization dans ce cas). J'enregistre tous les en-têtes que le système reçoit et, à partir du journal, je peux voir les autres en-têtes que j'ai listés dans la demande d'intégration mais pas dans l'autorisation. 

J'ai également essayé de créer un modèle de mappage avec Content-Type application/json et le modèle défini en tant que

  {
      "AccountID": "$context.identity.accountId",
      "Caller": "$context.identity.caller",
      "User": "$context.identity.user",
      "Authorization": "$input.params().header.get('Authorization')",
      "UserARN": "$context.identity.userArn"
  }

Cependant, les journaux d'arrière-plan montrent qu'il n'y a toujours pas d'en-tête d'autorisation ni de champ d'autorisation dans le corps JSON. Je ne peux pas non plus voir l'ARN de l'utilisateur. J'ai vu d'autres exemples et discussions dans lesquels des utilisateurs ont mentionné l'accès au champ Autorisation de l'objet événement transmis à une fonction Lambda, mais je n'utilise pas de fonction Lambda. 

Je me suis assuré de déployer la passerelle API dans les deux scénarios.

Est-ce que quelqu'un sait s'il existe un moyen de transmettre l'en-tête d'autorisation via la passerelle API à mon point de terminaison HTTP? Existe-t-il un autre moyen d'accéder au nom d'utilisateur ou à l'ID de l'appelant de l'API?


Edit - Voici un extrait du code que j'utilise pour frapper la passerelle API:

String awsAccessKey = "myaccesskey";
String awsSecretKey = "mysecretkey";

URL endpointUrl;
try {
    endpointUrl = new URL("https://<Host>/<path>/<to>/<resource>?startDate=20151201&endDate=20151231");
} catch(Exception e) {
    throw new RuntimeException("Unable to parse service endpoint: " + e.getMessage());
}

Date now = new Date();

SimpleDateFormat sdf1 = new SimpleDateFormat("yyyyMMdd'T'HHmmss'Z'");
sdf1.setTimeZone(new SimpleTimeZone(0, "UTC"));
String dateTS = sdf1.format(now);

String headerNames = "Host;x-amz-date";
String queryParameters = "endDate=20151231&startDate=20151201";

String canonicalRequest = "GET\n" +
        "/<path>/<to>/<resource>\n" +
        queryParameters + "\n" +
        "Host:<Host>\n" +
        "x-amz-date:" + dateTS + "\n" +
        "\n" +
        headerNames + "\n" +
        "<sha256 hash for empty request body>";

System.out.println(canonicalRequest);

SimpleDateFormat sdf2 = new SimpleDateFormat("yyyyMMdd");
sdf2.setTimeZone(new SimpleTimeZone(0, "UTC"));
String dateStr = sdf2.format(now);
String scope =  dateStr + "/us-east-1/execute-api/aws4_request";
String stringToSign =
        "AWS4-HMAC-SHA256\n" +
               dateTS + "\n" +
               scope + "\n" +
               "hex encoded hash of canonicalRequest";

System.out.println(stringToSign);

byte[] kSecret = ("AWS4" + awsSecretKey).getBytes();
byte[] kDate = HmacSHA256(dateStr, kSecret);
byte[] kRegion = HmacSHA256("us-east-1", kDate);
byte[] kService = HmacSHA256("execute-api", kRegion);
byte[] kSigning = HmacSHA256("aws4_request", kService);
byte[] signature = HmacSHA256(stringToSign, kSigning);
String credentialsAuthorizationHeader = "Credential=" + awsAccessKey + "/" + scope;
String signedHeadersAuthorizationHeader = "SignedHeaders=" + headerNames;
String signatureAuthorizationHeader = "Signature=" + "hex encoded signature";
String authorization = "AWS4-HMAC-SHA256 "
        + credentialsAuthorizationHeader + ", "
        + signedHeadersAuthorizationHeader + ", "
        + signatureAuthorizationHeader;

Map<String, String> headers = new HashMap<String, String>();
headers.put("x-amz-date", dateTS);
headers.put("Host", endpointUrl.getHost());
headers.put("Authorization", authorization);
headers.put("Content-Type", "application/json");

HttpURLConnection connection = null;
try {
    connection = (HttpURLConnection) endpointUrl.openConnection();
    connection.setRequestMethod("GET");

    for (String headerKey : headers.keySet()) {
        connection.setRequestProperty(headerKey, headers.get(headerKey));
    }
    connection.setUseCaches(false);
    connection.setDoInput(true);
    connection.setDoOutput(true);

    InputStream is;
    try {
        is = connection.getInputStream();
    } catch (IOException e) {
        is = connection.getErrorStream();
    }

    BufferedReader rd = new BufferedReader(new InputStreamReader(is));
    String line;
    StringBuffer response = new StringBuffer();
    while ((line = rd.readLine()) != null) {
        response.append(line);
        response.append('\r');
    }
    rd.close();
    System.out.println(response.toString());
} catch (Exception e) {
    throw new RuntimeException("Error: " + e.getMessage(), e);
} finally {
    if (connection != null) {
        connection.disconnect();
    }
}

Cela suffit pour s’authentifier avec succès et atteindre le point de terminaison HTTP sur le backend.

15
Nick

Comme indiqué dans les commentaires, l'en-tête Authorization contient des informations incomplètes pour vous permettre de déterminer qui est l'utilisateur. Je vous déconseille donc de suivre cette voie. En outre, si AWS_IAM auth est activé, l'en-tête d'autorisation sera utilisé par la passerelle API.

Si AWS_IAM auth est activé et que la signature est fournie correctement, les paramètres $ context.identity devraient reflètent les informations d'identification utilisées pour signer la demande.

Si vous utilisez la fonctionnalité d'invocation de test dans la console, voyez-vous les champs de contexte en cours de remplissage?

Mise à jour: Je ne parviens pas à reproduire ce problème . J'ai une API avec le modèle de mappage suivant:

#set($path = $input.params().path)
#set($qs = $input.params().querystring)
{
    "resource-path": "$context.resourcePath",
    "http-method": "$context.httpMethod",
    "identity": {
        #foreach($key in $context.identity.keySet())
            "$key": "$context.identity.get($key)"
        #if($foreach.hasNext), #end
        #end
    },
    "params": {
        #foreach($key in $path.keySet())
            "$key": "$path.get($key)"
        #if($foreach.hasNext), #end
        #end
    },
    "query": {
        #foreach($key in $qs.keySet())
            "$key": "$qs.get($key)"
        #if($foreach.hasNext), #end
        #end
    },
    "body": $input.json('$')
}

Et une fonction lambda qui renvoie simplement l'entrée en sortie. Lorsque je signe la demande et appelle l'API, je récupère les résultats attendus:

{
  "resource-path":"/iam",
  "http-method":"GET",
  "identity":{ 
    "cognitoIdentityPoolId":"",
    "accountId":"xxxxxxxx",
    "cognitoIdentityId":"",
    "caller":"AIDXXXXXXXXXXX,
    "apiKey":"",
    "sourceIp":"54.xx.xx.xx",
    "cognitoAuthenticationType":"",
    "cognitoAuthenticationProvider":"",
    "userArn":"arn:aws:iam::xxxxxxxx:user/hackathon",
    "userAgent":"Java/1.8.0_31",
    "user":"AIDXXXXXXXXXXXXXX"
  },
  "params":{},
  "query":{},
  "body":{}
}
7
Bob Kinney

Actuellement, l'en-tête Authorization ne peut être transmis que pour les méthodes ne nécessitant pas d'authentification AWS. Le processus de signature SigV4 s'appuie sur l'en-tête Authorization et nous ne l'exposons pas à des fins de sécurité. Si vous avez des données à envoyer (en plus de la signature SigV4), vous devrez envoyer un autre en-tête.

4
RyanG

Dans AWS API Gateway, le corps de la demande n'est pas pris en charge pour les méthodes GET.

1
jlorie

Dans la demande d'intégration, convertissez votre GET en POST en spécifiant POST comme méthode HTTP. Ensuite, spécifiez le modèle de mappage du corps proposé par @BobKinney.

De cette façon, le corps de la demande se propage correctement, mais le client continuera à faire une demande GET comme prévu.

1
malenkiy_scot