Skip to content

Instantly share code, notes, and snippets.

@antons
Last active March 13, 2019 07:10
Show Gist options
  • Save antons/3bb151c1659ef47cf1ad17e07ea28c71 to your computer and use it in GitHub Desktop.
Save antons/3bb151c1659ef47cf1ad17e07ea28c71 to your computer and use it in GitHub Desktop.
#import <Foundation/Foundation.h>
#import <CommonCrypto/CommonHMAC.h>
#import <CoreFoundation/CoreFoundation.h>
static NSString *Base64URLEncodedStringWithData(NSData *data) {
NSMutableString *string = [[data base64EncodedStringWithOptions:0] mutableCopy];
[string replaceOccurrencesOfString:@"+" withString:@"-" options:0 range:NSMakeRange(0, string.length)];
[string replaceOccurrencesOfString:@"/" withString:@"_" options:0 range:NSMakeRange(0, string.length)];
[string replaceOccurrencesOfString:@"=" withString:@"" options:0 range:NSMakeRange(0, string.length)];
return string.copy;
}
static NSData *DataWithHexString(NSString *hex) {
if (hex.length % 2 == 0) {
char buf[3];
buf[2] = '\0';
unsigned char *bytes = calloc(hex.length / 2, sizeof(unsigned char));
unsigned char *bp = bytes;
CFStringInlineBuffer inlineBuffer;
CFStringInitInlineBuffer((__bridge CFStringRef)hex, &inlineBuffer, CFRangeMake(0, hex.length));
for (CFIndex i = 0; i < hex.length; i += 2) {
buf[0] = CFStringGetCharacterFromInlineBuffer(&inlineBuffer, i);
buf[1] = CFStringGetCharacterFromInlineBuffer(&inlineBuffer, i + 1);
char *b2 = NULL;
*bp++ = strtol(buf, &b2, 16);
}
return [NSData dataWithBytesNoCopy:bytes length:hex.length / 2 freeWhenDone:YES];
}
return nil;
}
@implementation NSMutableURLRequest (Ghost)
+ (NSMutableURLRequest *)requestWithGhostAPIKey:(NSString *)APIKey APIURL:(NSURL *)APIURL endpoint:(NSString *)endpoint {
NSArray<NSString *> *APIKeyComponents = [APIKey componentsSeparatedByString:@":"];
NSString *kid = APIKeyComponents.count != 2 ? @"unknown" : APIKeyComponents[0];
NSString *secret = APIKeyComponents.count != 2 ? @"unknown" : APIKeyComponents[1];
NSDate *date = [NSDate date];
NSDateComponents *expirationDateComponents = [[NSDateComponents alloc] init];
expirationDateComponents.minute = 5;
NSDate *expirationDate = [[NSCalendar currentCalendar] dateByAddingComponents:expirationDateComponents toDate:date options:0];
NSError *JSONError;
NSData *headerData = [NSJSONSerialization dataWithJSONObject:@{@"alg": @"HS256", @"kid": kid, @"typ": @"JWT"} options:0 error:&JSONError];
NSData *payloadData = [NSJSONSerialization dataWithJSONObject:@{@"exp": @(lround(expirationDate.timeIntervalSince1970)), @"iat": @(lround(date.timeIntervalSince1970)), @"aud": @"/v2/admin/"} options:0 error:&JSONError];
NSMutableArray<NSString *> *tokenComponents = [[NSMutableArray alloc] init];
[tokenComponents addObject:Base64URLEncodedStringWithData(headerData)];
[tokenComponents addObject:Base64URLEncodedStringWithData(payloadData)];
NSData *data = [[tokenComponents componentsJoinedByString:@"."] dataUsingEncoding:NSUTF8StringEncoding];
NSData *secretData = DataWithHexString(secret);
NSMutableData *hmacData = [[NSMutableData alloc] initWithLength:CC_SHA256_DIGEST_LENGTH];
CCHmac(kCCHmacAlgSHA256, secretData.bytes, secretData.length, data.bytes, data.length, hmacData.mutableBytes);
[tokenComponents addObject:Base64URLEncodedStringWithData(hmacData)];
NSString *token = [tokenComponents componentsJoinedByString:@"."];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
request.URL = [NSURL URLWithString:[NSString pathWithComponents:@[@"/ghost/api/v2/admin/", endpoint]] relativeToURL:APIURL];
[request setValue:[NSString stringWithFormat:@"Ghost %@", token] forHTTPHeaderField:@"Authorization"];
return request;
}
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment