Skip to content

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
    }
}