Package: siwa
Sign in with AppleA library facilitating the server-side validation of Sign in with Apple (SIWA)
identity tokens.This library has a very narrow focus. It addresses a small subset of the
potential ways in which SIWA can be used. It does not attempt to address the
web-based SIWA flow. It abstracts away RSA256, JSON Web Tokens, and other
machinery used by SIWA, and attempts to provide a simple, clean interface for
application development.The problem this library solvesSuppose you have a macOS or iOS application. You present the SIWA interface
(the "Sign in with Apple" button) to a user usingAuthenticationServices.
They sign in. Your app is provided with an instance ofASAuthorizationAppleIDCredential(the "client side credential"), containing data describing the user.Suppose your system is controlled by a platform agnostic application
programming interface (API). You wish to create a new user account, or
sign in an existing user, via your API. Your app makes an HTTP request to your
API asking it to create a new user or sign in an existing user based on the
data contained the client side credential.How do you know the data was provided byAuthenticationServices, and not
just smashed together in a text editor? That is, how do you know the
data areauthenticon the server side?Apple facilitates the authentication of the credential by providing a blob
of data in theASAuthorizationAppleIDCredential.identityTokenproperty. This
identity token contains a JSON Web Token ("JWT"), a standardised format for
carrying credentials.Validation of the credential may be achieved via asymmetric cryptography, using
the RSA256 algorithm. Apple retains a secret encryption key, and publishes a
related public key. Apple creates signature (a large string of text) using two
inputs: Their private key, and the contents of the client side credential.External parties like your API can verify the authenticity of that signature,
and therefore the associated credential, using the public key published by
Apple.This library performs the following functions with respect to the above
process:Retrieves Apple's public keyParses an identity token (ASAuthorizationAppleIDCredential.identityToken)Provides a boolean flag asserting that the credential is valid or not validProvides convenient access to the content of the identity token (e.g
the user email address)InstallationInstall Sign in with Apple via PyPi:pip install siwaDependenciesThis library is heavily dependent onPyJWT. All validation of identity
tokens is peformed byPyJWT.Marshalling of the Apple public RSA key into PKS12 format is performed by
thePythonRSAlibrary.UsagefromsiwaimportIdentityToken,KeyCachecache=KeyCache()# The cache is optional but will reduce the time taken# to validate tokens using the same public keytoken=IdentityToken.parse(data=json_string)token_is_valid=token.is_validly_signed(key_cache=cache)# if `token_is_valid` is True, you can confidently proceed with the credential# Useful properties (see type reference for more):print(token.payload.email)print(token.payload.unique_apple_user_id)Public Type ReferenceKeyCacheA store for Apple's public key. If you supply aKeyCacheinstance toIdentityToken.is_validly_signed, you can reduce the time it takes to
validate the key, asIdentityTokenwill retrieve the public key from theKeyCacherather than making an HTTP request to Apple's servers.Example Usagekey_cache=KeyCache()IdentityTokenRepresents a SIWA identity token. Initialise with.parse(:Union[bytes, str])and then check validity with the.is_validly_signedinstance method.MethodsClass.parse(data: Union[bytes, str]) -> IdentityTokenInstance.is_validly_signed(audience:str,key_cache:Optional[KeyCache]=None,ignore_expiry:bool=False)->boolCall.is_validly_signedto check if a token is valid. Optionally pass an
instance ofKeyCacheto improve performance for repeated checks.Optionally specifyignore_expiry=trueif you do not wish for an expired
token to be considered invalid (useful for testing purposes).Properties.payload: PayloadExample UsagefromsiwaimportIdentityTokenimportjson# Suppose you have a file named token.json containing a SIWA token:withopen('token.json','r')asrfile:json_string=json.loads(rfile.read())token=IdentityToken.parse(data=json_string)token_is_valid=token.is_validly_signed(audience='blinkybeach.Makara')print('The token is{v}'.format(v=('valid'iftoken_is_validelse'not valid')))PayloadA store of data provided by Apple, describing the user.Propertiesunique_apple_user_id:strexpires_utc_seconds_since_epoch:intissued_utc_seconds_since_epoch:intemail:stremail_is_private:Optional[bool]real_person:Optional[RealPerson]Example Usage# Using `token` from the above `IdentityToken` examplepayload=token.payloadprint('The user\'s email is{e}and unique ID{i}'.format(e=payload.email,i=payload.unique_apple_user_id))RealPersonAn enumeration of possible values provided by Apple.CasesUNSUPPORTED
UNKNOWN
LIKELY_REALTestingTo test the library, create a file that contains a valid SIWA identity token.
For example, one that you have obtained fromAuthenticationServicesin
Xcode.Runtest.py, passing command line arguments:--example-jwt-file: the relative path to your identity token file--audience: the audience for the token$ python3 test.py --example-jwt-file example/jwt/file --audience \
blinkybeach.MakaraContact@hugh_jeremyon Twitter or emailhugh@blinkybeach.com