Code Challenge
Using Objective C
To utilize the Authorize APIs effectively, generating a Code Challenge is a prerequisite. However, this necessitates the prior generation of a Code Verifier.
Generating a Code Verifier
The Code Verifier stands as a cryptographically random string produced by your product. It is crucial that this string spans between 43 and 128 characters, comprised solely of characters from the URL and filename-safe alphabet ([A-Z], [a-z], [0-9], "-", "_", ".", "~"). The utmost care should be exercised in handling this sensitive data, refraining from transferring it from your product.
AuthProofKey.h
#import <Foundation/Foundation.h>
@interface AuthProofKey : NSObject
// Properties
@property (strong, nonatomic, readonly) NSString *codeVerifier;
@property (strong, nonatomic, readonly) NSString *codeChallenge;
// Restricting the usage of init method.
-(instancetype) init __attribute__((unavailable("Unavailable, use genera te method instead")));
+ (id) generate;
@end
AuthProofKey.m
#import "AuthProofKey.h"
#import <CommonCrypto/CommonDigest.h>
@interface AuthProofKey()
@property (strong, nonatomic) NSString *codeVerifier;
@property (strong, nonatomic) NSString *codeChallenge;
@end
@implementation AuthProofKey
static int LENGTH = 80;
static NSString *const SHA_256 = @"SHA-256";
- (id)initWithCodeVerifier: (NSString *)codeVerifier codeChallenge: (NSString *)codeChallenge {
self = [super init];
if (self) {
self.codeChallenge = codeChallenge;
self.codeVerifier = codeVerifier;
}
return self;
}
/**
To provide singleton
**/
+(id) generate {
static AuthProofKey *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSString *verifier = [self getSecureRandom];
NSString *challenge = [self sha256Hash:verifier];
sharedInstance = [[self alloc]initWithCodeVerifier:verifier codeChallenge:challenge];
});
return sharedInstance;
}
/**
To generate code verifier
**/
+(NSString *)getSecureRandom {
NSMutableData *data = [NSMutableData dataWithLength:LENGTH];
NSAssert(SecRandomCopyBytes(kSecRandomDefault, LENGTH, data.mutableBytes) == 0, @"Unable to generate random bytes: %d", errno);
return [self urlSafeBase64encode:data];
}
/**
To generate code challenge from supplied verifier
**/
+(NSString *)sha256Hash:(NSString *)value {
NSData *inputData = [NSData dataWithBytes:value.UTF8String length:value.length];
NSMutableData *output = [NSMutableData dataWithLength:CC_SHA256_DIGEST_LENGTH];
CC_SHA256(inputData.bytes, (CC_LONG) inputData.length, output.mutableBytes);
return [self urlSafeBase64encode:[output copy]];
}
/*
* Standard iOS implementation of base64 encoding is not URL safe.
* The Base64url encoded string should not contain the following characters:
“=”, “+”, or “/”.
* param The NSData object that we want to encode.
* return The URL safe base64 encoded NSString.
*/
+ (NSString *)urlSafeBase64encode:(NSData *)data {
NSString *base64String = [data base64EncodedStringWithOptions:0];
base64String = [base64String stringByReplacingOccurrencesOfString:@"/" withString:@"_"];
base64String = [base64String stringByReplacingOccurrencesOfString:@"+" withString:@"-"];
while ([base64String hasSuffix:@"="]) {
base64String = [base64String substringToIndex:[base64String length] - 1];
}
return base64String;
}
@end
Using Swift
To utilize the Authorize APIs effectively, generating a Code Challenge is a prerequisite. However, this necessitates the prior generation of a Code Verifier.
Generating a Code Verifier
The Code Verifier stands as a cryptographically random string produced by your product. It is crucial that this string spans between 43 and 128 characters, comprised solely of characters from the URL and filename-safe alphabet ([A-Z], [a-z], [0-9], "-", "_", ".", "~"). The utmost care should be exercised in handling this sensitive data, refraining from transferring it from your product.
AuthProofKey.swift
import Foundation
import CommonCrypto
class AuthProofKey:NSObject {
//to provide singleton instance of AuthProofKey
static let sharedInstance: AuthProofKey = {
let verifier = generateRandomBytes()
let challenge = sha256Hash(value: verifier)
let instance = AuthProofKey(codeVerifier: verifier!, codeChallenge:challenge)
// setup code
return instance
}()
var codeVerifier:String!;
var codeChallenge:String!;
static var LENGTH = 80;
internal required init(codeVerifier: String?, codeChallenge: String?) {
super.init()
self.codeChallenge = codeChallenge
self.codeVerifier = codeVerifier
}
//to generate codeverifier
class func generateRandomBytes() -> String? {
var keyData = Data(count: LENGTH)
let result = keyData.withUnsafeMutableBytes {
(mutableBytes: UnsafeMutablePointer<UInt8>) -> Int32 in SecRandomCopyBytes(kSecRandomDefault, LENGTH, mutableBytes)
}
if result == errSecSuccess {
return self.urlSafeBase64encode(keyData)
}
else {
print("Problem generating random bytes")
return nil
}
}
//to get code challenge for supplied verifier
class func sha256Hash(value: String?) -> String? {
let data:Data = (value?.data(using: .utf8))!
var hash = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH))
data.withUnsafeBytes {
_ = CC_SHA256($0, CC_LONG(data.count), &hash)
}
return self.urlSafeBase64encode(Data(bytes: hash))
}
/**
* Standard iOS implementation of base64 encoding is not URL safe.
* The Base64url encoded string should not contain the following characters: “=”, “+”, or “/”.
* param The Data object that we want to encode.
* return The URL safe base64 encoded String.
**/
class func urlSafeBase64encode(_ data: Data?) -> String? {
var base64String = data?.base64EncodedString(options: [])
base64String = base64String?.replacingOccurrences(of: "/", with: "_")
base64String = base64String?.replacingOccurrences(of: "+", with: "-")
while (base64String?.hasSuffix("="))! {
base64String = (base64String as NSString?)?.substring(to:(base64String?.count ?? 0) - 1)
}
return base64String
}
}