Last active
December 17, 2024 00:14
-
-
Save andyzinsser/8044165 to your computer and use it in GitHub Desktop.
Full flow of authenticating a Game Center Player on a third party server.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// request Game Center verification data for localPlayer from apple | |
- (void)authenticateGameCenterPlayer:(GKLocalPlayer *)localPlayer | |
{ | |
[localPlayer generateIdentityVerificationSignatureWithCompletionHandler:^(NSURL *publicKeyUrl, NSData *signature, NSData *salt, uint64_t timestamp, NSError *error) { | |
if (error) { | |
NSLog(@"ERROR: %@", error); | |
} | |
else { | |
// package data to be sent to server for verification | |
NSDictionary *params = @{@"publicKeyUrl": publicKeyUrl, | |
@"timestamp": [NSString stringWithFormat:@"%llu", timestamp], | |
@"signature": [signature base64EncodedStringWithOptions:0], | |
@"salt": [salt base64EncodedStringWithOptions:0], | |
@"playerID": localPlayer.playerID, | |
@"bundleID": [[NSBundle mainBundle] bundleIdentifier]}; | |
// setup AFNetworking request to send the data to the server | |
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager]; | |
[manager POST:APILinkWithGameCenterURL # update to your own service URL | |
parameters:params | |
constructingBodyWithBlock:nil | |
success:^(AFHTTPRequestOperation *operation, id responseObject) { | |
NSLog(@"%@", responseObject); | |
} | |
failure:^(AFHTTPRequestOperation *operation, NSError *error) { | |
NSLog(@"%@", error); | |
}]; | |
} | |
}]; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# NOTE: using django rest framework | |
class authenticateWithGameCenter(APIView): | |
def post(self, request): | |
response = {'errors': []} | |
# validation | |
publicKeyUrl = request.DATA.get('publicKeyUrl', None) | |
if publicKeyUrl is None: | |
response['errors'].append('publicKeyUrl is required.') | |
signature = request.DATA.get('signature', None) | |
if signature is None: | |
response['errors'].append('signature is required.') | |
salt = request.DATA.get('salt', None) | |
if salt is None: | |
response['errors'].append('salt is required.') | |
timestamp = request.DATA.get('timestamp', None) | |
if timestamp is None: | |
response['errors'].append('timestamp is required.') | |
playerID = request.DATA.get('playerID', None) | |
if playerID is None: | |
response['errors'].append('playerID is required.') | |
bundleID = request.DATA.get('bundleID', None) | |
if bundleID is None: | |
response['errors'].append('bundleID is required.') | |
if len(response['errors']) > 0: | |
return APIResponse(response) | |
decoded_sig = signature.decode('base64') | |
decoded_salt = salt.decode('base64') | |
# Download and read the .cer from apple and extract the public key | |
r = requests.get(publicKeyUrl, stream=True) | |
local_filename = publicKeyUrl.split('/')[-1] | |
with open(local_filename, 'wb') as f: | |
for chunk in r.iter_content(chunk_size=1024): | |
if chunk: | |
f.write(chunk) | |
f.flush() | |
der = open(local_filename, 'r').read() | |
x509 = crypto.load_certificate(crypto.FILETYPE_ASN1, der) | |
payload = playerID.encode('UTF-8') + bundleID.encode('UTF-8') + struct.pack('>Q', int(timestamp)) + decoded_salt | |
try: | |
verified = crypto.verify(x509, decoded_sig, payload, 'sha1') | |
logger.info('Successfully verified certificate with signaure') | |
except Exception as err: | |
logger.info(err) | |
response['errors'].append(err) | |
# TODO: | |
# Verify that a hash value of the payload matches the signature parameter provided by apple. | |
# If the generated and retrieved signatures match, the local player has been authenticated. | |
# Get or create a user for the given playerID | |
# authenticate a session for this player | |
# return the user | |
return APIResponse(response) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@Maximusya isn't that the point of having a variable publicKeyUrl..? Current url is https://static.gc.apple.com/public-key/gc-prod-2.cer and if that needs to be revoked or renewed the new certificate would presumably be https://static.gc.apple.com/public-key/gc-prod-3.cer and so on..