I am seeing this error when I try to hit endpoint using postman:
{
"errors": [
"token contains an invalid number of segments"
]
}
Console:
TypeError: Cannot read property 'jwt' of undefined
The question is a bit vague, but in this case - TypeError: Cannot read property 'jwt' of undefined.
The object that should have the property jwt, is undefined. So the token itself doesn't exist, and neither does the object that is supposed to hold it.
More generally, this error may occur if the token is malformed.
It should have 3 segments, like this [separated by . ]-
eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk
Each of those segments can be decoded by base64 to inspect the header, payload, and signature.
Which results in this -
from - https://jwt.io/
More information from https://www.ibm.com/docs/en/cics-ts/6.1_beta?topic=cics-json-web-token-jwt
Header
The header typically consists of two parts: the type of the token, which is JWT, and the algorithm that is used, such as HMAC SHA256 or RSA SHA256. It is Base64Url encoded to form the first part of the JWT.
Payload
The payload contains the claims. There is a set of registered claims, for example: iss (issuer), exp (expiration time), sub (subject), and aud (audience). These claims are not mandatory but recommended to provide a set of useful, interoperable claims. The payload can also include extra attributes that define custom claims, such as employee role. Typically, the subject claim is used to create the OpenID Connect user subject. However, the Liberty JVM server can be configured to use an alternative claim. The payload is Base64Url encoded to form the second part of the JWT.
Signature
To create the signature part, the encoded header and encoded payload are signed by using the signature algorithm from the header. The signature is used to verify that the issuer of the JWT is who it says it is and to ensure that the message wasn't changed along the way.
More information about the spec here -
https://datatracker.ietf.org/doc/html/rfc7519#page-7
Related
I am working with Elixir using the JOSE package. I have an ALB that is using the Authentication Listener rule, and the data I am getting in the x-amzn-oidc-data is supposed to be a JWT containing the user claims. However, while the value is mostly in JWT formatting, it is base64 URL encoded, but with padding. As such, JOSE is unable to verify most of the tokens coming in (if a token arrives without padding, JOSE verifies it no problem).
I thought I could fix this by simply stripping the = characters from the token in the header, payload and signature, however no combination of that seems to work.
I've used the jwt.io to try and debug the token, and it complains about the signature becoming invalid if I attempt to strip = characters from the payload (only?). However, I have tried validating the token using the Python JOSE library and the Javascript JOSE library, and both are able to. Again though, if I strip the padding characters from the payload, both those libraries tell me the signature verification fails. I have to leave it in for the signature to go through.
Is there a way to massage this token into a format that doesn't include the padding, and could be verified by the Elixir JOSE package? I've tried to reverse-engineer how these libraries are able to decode the tokens while others fail, but I haven't had any luck.
Just for a visual example, this is how the tokens should look
eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0.tyh-VfuzIxCyGYDlkBA7DfyjrqmSHu6pQ2hoZuFqUSLPNY2N0mpHb3nk5K17HWP_3cYHBw7AhHale5wky6-sVA
but the ALB sends me things that look like (note the added padded characters in the payload and signature)
eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0==.tyh-VfuzIxCyGYDlkBA7DfyjrqmSHu6pQ2hoZuFqUSLPNY2N0mpHb3nk5K17HWP_3cYHBw7AhHale5wky6-sVA==
As you mentioned you have to leave the = in for the signature verification to succeed.
The reason JOSE library fails verification is it reconstructs the signing input by decrypting the header + payload, then encrypting them again (This time omitting = characters).
https://github.com/potatosalad/erlang-jose/blob/main/src/jws/jose_jws.erl#L378
You can get around this by manually constructing the signing input, and calling the alg library directly, something like this:
#data from x-amzn-oidc-data header, key from "https://public-keys.auth.elb." <> aws_region <> ".amazonaws.com/" <> kid
[header, payload, signature] = String.split(data, ".") |> Enum.map(fn part -> :jose_jwa_base64url.decode(part) end)
signing_input = Base.url_encode64(header) <> "." <> Base.url_encode64(payload)
encoded_key = key |> JOSE.JWK.from_pem() |> JOSE.JWK.to_record()
:jose_jws_alg_ecdsa.verify(encoded_key, signing_input, signature, :ES256)
The project uses a swagger.
There is the following code.
#swagger_auto_schema(
manual_parameters=[
Parameter('download', IN_QUERY,
'Set `Content-Disposition=attachment` to make browser to download file'
'instead of showing it.',
type='bool'),
Parameter('share_id', IN_PATH, type='uuid')
],
security=[],
responses={'400': 'Validation Error (e.g. base64 is wrong)',
'200': VideoSerializer}
)
Please explain what each argument is responsible for.
I read the documentation, but understood little ...
Particularly interested in '200': VideoSerializer
responses
The responses argument is a dictionary of possible responses that this endpoint can return.
400 and 200 are HTTP response codes, Bad Request and OK respectively.
In this case, this means that this endpoint can generate two types of responses:
Bad request which will also return (as described) a Validation Error which means that something in the request was incorrect, which means it could not be handled correctly.
OK, which means the request is correct, and everything was handled correctly. VideoSerializer means that a response will be given with accordance to the structure of the VideoSerializer, which defines a collection of fields.
The other two arguments:
manual_parameters
This a custom list of parameters that can be added to the request to customize the response.
In this case, two parameters are defined:
download : A query parameter of type bool. Query parameters are passed like this : `example.com?query_parameter=true
share_id, a path parameter of type 'uuid'. Path parameters are passed like this : example.com/path_parameter
security
A list of security schemes that the request must adhere to. Used for instance for Basic authentication.
I want to return a pdf from a AWS Lambda function, and I use API Gateway for calling it from a any browser.
I have a aws lambda function in c# that returns an API Gateway Response which body is a pdf in base64 encoded string.
So far the endpoint returns a file with .pdf extension but not in binary. Instead, is a text file with the base64 string.
The headers i'm returning from the c# code are:
var headersDic = new Dictionary<string, string>();
headersDic.Add("Content-type", "application/pdf");
headersDic.Add("Content-disposition", "attachment; filename=file.pdf");
I converted manually the base64 string to binary file and opened it as a pdf, and it works, I mean, the base64 string is correct so I assume the problem is being the API Gateway.
In the integration response console of the API Gateway, I got this:
But i'm not able to make it work.
I also have binary media types enabled.
Actually, you do not have to disable Lambda Proxy Integration, nor do you have to specify "*/*" as a Binary Media Type, nor do you have to have your Lambda code convert the output to Base64, nor do you have to modify LambdaEntryPoint in order to get this to work.
Although some sources indicate that the "*/*" entry is necessary, the answer lies in the comment accompanying the Binary Media Types setting:
"API Gateway will look at the Content-Type and Accept HTTP headers to
decide how to handle the body."
In other words, for some odd reason, rather than just paying attention to the returned Content-Type in applying the Binary Media Types list, the gateway also takes into account the requesting client's Accept header. Most clients default to Accept:*/*, thus the origin of the workaround, and the most common explanation as to why this list doesn't seem to work. Not only does the client's Accept header seem to be given more credence than the returned Content-Type value here, there is no semantic assessment of "*/*". It is not evaluated as a wildcard, which should match any content type. Thus, an exact match of said value, literally "*/*", winds up being needed in the Binary Media Types list.
A simple solution (one which works for me) is to:
Have the client, instead of specifying "*/*" in its Accept header, specify the actual expected MIME type. (Do not specify multiple Accept types, as only the first listed type is evaluated.)
Include the same MIME type in the Binary Media Types list. Do not specify "*/*".
Have the Lambda function return a non-Base64-encoded binary file, specifying its actual MIME type in the Content-Type header.
For instance, in C#, by returning a FileContentResult from the ControllerBase.File() method, specifying the appropriate MIME content type, as in:
return File(pdfContents, "application/pdf"); //pdfContents contains raw binary data
API Gateway will match the data type to its Binary Media Types list and regard the returned data as binary. So far, I have not needed to insert RegisterResponseContentEncodingForContentType() in LambdaEntryPoint.Init().
I don't know exactly what I did, but I deleted the 'ALL' method, and created the 'GET' method with this config and now it works.
You also need to declare the content type like the following in your LambdaEntryPoint.cs
See this documentation: https://github.com/aws/aws-lambda-dotnet/blob/master/Libraries/src/Amazon.Lambda.AspNetCoreServer/README.md
You have to tell Lambda you're going to return something besides strings.
public class LambdaEntryPoint : Amazon.Lambda.AspNetCoreServer.APIGatewayProxyFunction
{
protected override void Init(IWebHostBuilder builder)
{
builder
.UseStartup<Startup>();
****IMPORTANT PART HERE****
RegisterResponseContentEncodingForContentType("application/pdf",
ResponseContentEncoding.Base64);
}
}
Response Headers
'Content-Type': 'application/pdf'
'Content-disposition', 'attachment; filename=fileName.pdf'
Response to Client
{
statusCode: 200
headers: responseHeader,
body: pdfContent.toString(base64),
isBase64Encoded: true,
}
API Gateway Binary Media Types
Hope it will help
What can be possible values of response-content-type in object.get API.
It's description says:
Sets the Content-Type header of the response.
Type: String
But I don't see possible values in the docs.
Why I ask is one of the possible value might help in solving this issue.
There is no list of the possible values, because Content-Type is provided by you, when you upload the object. The value can be any syntactically valid MIME type, expressed as type "/" subtype *( ";" parameter).
If you provide nothing with the upload, the value is set to the default generic MIME type binary/octet-stream which specifies that the object is to be understood by the user agent as a stream of octets (bytes) with no declared meaning.
I'm going to write a program can post and read messages from SQS with authentication and I've read the document from here
Link: Query Request Authentication
I have successfully written the process which post a message to specified queue follow by the document. But I always get 403 error when I try to receive message from queue. And I found the signature string rules are different for POST and GET methods.
the signature string is:
GET\n
sqs.us-east-1.amazonaws.com\n
/<My Account Id>/<Queue Name>\n
AWSAccessKeyId=<My Access Key>
&Action=ReceiveMessage
&MaxNumberOfMessages=10
&VisibilityTimeout=600
&AttributeName=All
&Expires=2012-04-01T11%3A29%3A24Z
&SignatureMethod=HmacSHA1
&SignatureVersion=2
&Version=2011-10-01
and the url is
https://sqs.us-east-1.amazonaws.com/<My Account Id>/<Queue Name>?
Action=ReceiveMessage
&MaxNumberOfMessages=10
&VisibilityTimeout=600&AttributeName=All
&Version=2011-10-01
&Expires=2012-04-01T11%3A29%3A24Z
&Signature=<BASE64 encoded HmacSHA1 digist with signature string and my security key>
&SignatureVersion=2
&SignatureMethod=HmacSHA1
&AWSAccessKeyId=<My Access Key>
And I always get the 403 forbidden error:
<ErrorResponse xmlns="http://queue.amazonaws.com/doc/2011-10-01/">
<Error>
<Type>Sender</Type>
<Code>SignatureDoesNotMatch</Code>
<Message>
The request signature we calculated does not match the signature you provided. Check your AWS Secret Access Key and signing method. Consult the service documentation for details.
</Message>
<Detail/>
</Error>
<RequestId>16f6e910-62e6-4259-8c09-0358b84cbe60</RequestId>
</ErrorResponse>
Is there anyone can tell me how can I deal with it? Thanks a lot
The error message tells you that the signature is being calculated wrong. This is really tough to debug. I spent hours on it the first time I tried it. There's an example signed SQS request at http://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/MakingRequests_MakingQueryRequestsArticle.html. You should put those parameters in your program, calculate the signature, and try finding bugs into your program creates the same signature.
Specific problems I had, and fixes for them included:
Sorting the query parameters correctly. They must be in ascending order when creating the string to sign. Your example URL does not show them in order. Did you sort them differently when creating the string to sign?
URI encoding properly. Every parameter must be URI encoded in the string to sign. Your sample URL does have URI encoding, so this probably isn't your issue. But make sure you're not double-encoding.
Padding the base64 signature. At least some AWS services insist that the signature be a multiple of four characters long. Two-thirds of the time a base64 encoding will be too short, and need one or two equal signs appended to it. Most base64 encoding libraries do that for you, but not all.
Of course, the easiest thing is to use somebody else's library to make the requests, but what's the fun in that? Good luck debugging this.
It's most likely the parameter order: when assembling the signature version 2 string, at the last step the Amazon documentation specifies:
Add the query string components (the name-value pairs, not including
the initial question mark (?) as UTF-8 characters which are URL
encoded per RFC 3986 (hexadecimal characters must be uppercased) and
sorted using lexicographic byte ordering. Lexicographic byte ordering
is case sensitive.
I've spent two days debugging this same "SignatureDoesNotMatch" issue by checking my HMAC, BASE64 and URL encoding procedures and it was just a problem of parameter order.
The documentation should emphasize this issue more; if you use unordered parameter strings (e.g. the same one in the request URL, like those found in the documentation examples), you're going to get this non-intuitive error from the server.