I'm implementing JSON Web Token authentication on the iOS (7) cient-side. It's working nicely. My app rceives tokens, and can make authenticated calls to my server with them.
Now, I want my client side code to check for an expiration date on the token so it can know when to re-authenticate. Checking for the expiration date on a JWT auth token is straightforward. The authorization token is 3 base64 encoded JSON blobs, separated by a '.' - The expiration timestamp is in the middle blob, in a field called ext
. It's seconds since unix epoch.
So my code's looking like so:
- (NSDate*) expirationDate { if ( !_tokenAppearsValid ) return nil; if ( !_parsedExpirationDate ) { // // Token is three base64 encoded payloads separated by '.' // The payload we want is the middle one, which is a JSON dict, with // 'exp' being the unix seconds timestamp of the expiration date // Returning nil is appropriate if no 'exp' is findable // NSArray *components = [self.token componentsSeparatedByString:@"."]; NSString *payload = components[1]; NSData* payloadJsonData = [[NSData alloc] initWithBase64EncodedString:payload options:NSDataBase64DecodingIgnoreUnknownCharacters]; NSError* jsonError = nil; NSDictionary* payloadJson = [NSJSONSerialization JSONObjectWithData:payloadJsonData options:0 error:&jsonError]; if ( payloadJson ) { if ( payloadJson[@"exp"] ) { NSTimeInterval timestampSeconds = [payloadJson[@"exp"] doubleValue]; _expirationDate = [NSDate dateWithTimeIntervalSince1970:timestampSeconds]; } } _parsedExpirationDate = YES; } return _expirationDate; }
The problem is simple. The middle base64 blob, when parsed by NSData -initWithBase64EncodedString is nil
- and that's bad.
I've checked the base64 blob and it seems to be valid. My server's returning dummy data for the moment so here's an example blob: eyJlbWFpbCI6ImZvb0BiYXIuYmF6IiwiYWNjb3VudElkIjoiMTIzNDUtNjc4OTAtYmFyLWJheiIsImV4cCI6MTM5MDkxNTAzNywiaWF0IjoxMzkwOTE0MTM3fQ
It decodes to:
{"email":"[email protected]","accountId":"12345-67890-bar-baz","exp":1390915037,"iat":1390914137}
I tested it here: http://www.base64decode.org
I've used NSData's base64 methods elswhere in my app with success - I don't think I'm doing anything particularly broken here. But I'm all ears! Any ideas?
The base 64 digits in ascending order from zero are the uppercase characters 'A' to 'Z', lowercase characters 'a' to 'z', numerals '0' to '9', and the symbols '+' and '/'. % is not allowed in base64 encoding.
In base64 encoding, the character set is [A-Z, a-z, 0-9, and + /] . If the rest length is less than 4, the string is padded with '=' characters. ^([A-Za-z0-9+/]{4})* means the string starts with 0 or more base64 groups.
To decode with base64 you need to use the --decode flag. With encoded string, you can pipe an echo command into base64 as you did to encode it. Using the example encoding shown above, let's decode it back into its original form. Provided your encoding was not corrupted the output should be your original string.
Your Base64 string is not valid. It must be padded with =
characters to have a length that is a multiple of 4. In your case: "eyJlbWFp....MTM3fQ=="
.
With this padding, initWithBase64EncodedString
decodes the Base64 string correctly.
Although Martin's answer is correct, here is a quick and correct(!) way to fix the problem:
NSString *base64String = @"<the token>"; NSUInteger paddedLength = base64String.length + (4 - (base64String.length % 4)); NSString* correctBase64String = [base64String stringByPaddingToLength:paddedLength withString:@"=" startingAtIndex:0];
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With