How do I decode/decrypt Apple Server-to-Server Notifications? - apple-push-notifications

My website supports Sign In with Apple.
In the configurations of this service, I have an endpoint here:
What I receive in this endpoint is a JSON like this:
"{"payload":"eyJraW...................NLnyA"}
However, I don't find absolutely anywhere how to decrypt/decode this payload...
The "Learn more" link sends me here: https://developer.apple.com/help/account/configure-app-capabilities/about-sign-in-with-apple
The page below this one is this: https://developer.apple.com/help/account/configure-app-capabilities/enabling-server-to-server-notifications
Nowhere I see how to interpret these messages...
Does anyone know what do I need to do to read these payloads?

It looks like the general procedure for Server-to-Server notifications are outlined here. This is what the docs have to say:
These notifications contain a cryptographically signed payload, in JSON Web Signature (JWS) format, signed by Apple’s private key. After your server receives a notification, examine the JWS payload and use the algorithm specified in the header’s alg parameter to validate the signature. For more information, see Fetch Apple’s public key for verifying token signature.
So, this payload is really just a JWT (verify/decode with the JWT library of your choice, there are many to choose from). Because anyone can access your endpoint, you need to verify that the token is really from Apple. Note: do not try to decode the JWT yourself. Because of security concerns, it is better to let a library do it for you.
After validating the token signature, your server performs work according to the type value in the events claim of the token. The notification payload object contains information about user-initiated account modification events.
The decoded JWT will contain something like this (example is from the docs):
{
"iss": "https://appleid.apple.com",
"aud": "com.mytest.app",
"iat": 1508184845,
"jti": "abede...67890",
"events": {
"type": "email-enabled",
"sub": "820417.faa325acbc78e1be1668ba852d492d8a.0219",
"email": "ep9ks2tnph#privaterelay.appleid.com",
"is_private_email": "true"
"event_time": 1508184845
}
}
events.type has the event that happened (full list is here), and the rest of the token contains everything else you'll need.

#Michael M.'s answer helped me understanding that this payload is basically a JWT.
This is what we need to do (minimal example):
use Firebase\JWT\JWK;
use Firebase\JWT\JWT;
$json = json_decode(file_get_contents('php://input'));
$publicKeys = json_decode(file_get_contents('https://appleid.apple.com/auth/keys'), true);
$decodedPayload = JWT::decode($json->payload, JWK::parseKeySet($publicKeys));
var_dump($decodedPayload);

Related

How does validation of JWT distinguish difference between token types?

I am playing around with building a custom Oauth2/OpenID library, and is having thoughts about validating the tokens.
I am using only JWT for all token types (Access, Refresh & ID), but I am thinking; How would the resource server validate ex. the access token, and make sure it is only access tokens from the issuer being accepted, and not refresh or ID tokens, since they would also be valid, hence they come from the same trusted issuer?
Likewise, how would make sure, the token sent with a refresh grant, is not just an valid access token, since it would also be verified...
I know an easy fix would be just making a custom claim, describing what kind of token it is, or using different signatures for each, but is there a "right" way of doing it?
One way to separate the ID token from the Access token is by looking at the typ claim in the JWT-header.
Some IdentityProviders uses the at+jwt typ to indicate that the token is an access token that follows certain rules. But this is not a mandatory thing to follow.
{
"typ":"at+JWT",
"alg":"RS256",
"kid":"RjEwOwOA"
}
Otherwise they can look at the claims inside the token to determine if it is an access or ID-token. The token-signature itself can't be used to determine the token type.
You can read more about the standard for access token here and here
Refresh and reference tokens are typically not in a JWT format, instead they are more like a random string.

jwt integration django drf

i am trying to implement jwt on my django app but i have some confusion
when i make post request on postman with email pass:
http:www.127.0.0.1:8000/token/
i get refresh and access token:
{
"refresh": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoicmVmcmVzaCIsImV4cCI6MTU3NDUwMTcxNSwianRpIjoiNDg5YjjZmQyY2IwNDI4YjgxMzg4NDU1YmQ1ZGM3NDYiLCJ1c2VyX2lkIjo2fQ.5Bms8FiqOdlIeyi1k1cRcfLC1qmHEH05MysZnzMcKCA",
"access": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNTc0NDE1NjE1LCJqdGkiOiI1OTBkMGjZjJlYmE0YjNmOTE1MzI5NTEyOGExNzUyMCIsInVzZXJfaWQiOjZ9.7HZYpD-mCp5li4PnGSuwQGyVa7nsDYvUVWaxdyKYk-E"
}
and when i make this:
http:www.127.0.0.1:8000/rest-auth/login/
i get access token:
{
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjo2LCJ1c2VybmFtZSI6ImFrYXNAbWFpbC5jb20iLCJleHAiOjE1NzQ0MTU4NTYsImVtYWlsIjoiYWthc0BtYWlsLmNvbSIsIm9yaWdfaWF0IjoxNTc0NDE1Nzk2fQ.DEpZNq-kFFqYb3ZfAiYDB1CigfY36qpS9o-T7rJ5-_I",
"user": {
"email": "akas#mail.com",
"password": "pbkdf2_sha256$150000$OdHDeKGCViax$BvOXGFheSJoNb692ZVYxoWnZxK2xZQpvbu7HA/SC5Po="
}
}
then i can make get request when i add this access token in header authorization
i don't understand whats the difference between these two access token?
Your first pair of tokens aren't valid, and they're structurally extremely different from your second token.
A JWT has three parts, separated by periods. (RFC 7519 has the full specification.) The first part is a header; the third part is a signature; but the middle part is a set of claims that's really the interesting content of the token. The first two parts are just base64 encoded so you can just decode them to see what's inside. The claims of your first access token look like
{"token_type":"access","exp":1574415615,"jti":"590d0hٌ?X?M?َLML̎MLL?LM?L?\?\???
but the second one is
{"user_id":6,"username":"akas#mail.com","exp":1574415856,"email":"akas#mail.com","orig_iat":1574415796}
This suggests to me that you have two different JWT implementations in your application and also that one of them is configured wrong; since the first token can't be decoded as JSON it can't be validly used.
There's one other interesting thing that may be a hint when debugging. If I take your access token and copy it into the decoder on the https://jwt.io/ front page, and I delete the characters jZj in the middle of the token, then I get
{
"token_type": "access",
"exp": 1574415615,
"jti": "590d0beba4b3f9153295128a17520",
"user_id": 6
}
which (up to the signature) could potentially work. The different claims between the two tokens still look odd, though.
("jti" is a standard claim for a unique identifier for the token; the most useful application of it is keeping a blacklist of revoked tokens. There's also a standard "sub" claim identifying the subject of a token which would probably take the place of your "user_id".)

beaconinfo.getforobserved always returning an empty response

Here is my request.
POST https://proximitybeacon.googleapis.com/v1beta1/beaconinfo:getforobserved?key=<API_KEY>
with POST data
{
"observations": [
{
"advertisedId": {
"type": "EDDYSTONE",
"id": "XcM0h/AuR31AWAEXxV59Xw=="
},
"timestampMs": "2017-11-28T12:11:23.045123456Z"
}
],
"namespacedTypes": [
"*"
]
}
I've checked the beacon dashboard to see if the beacon has any attachments to it. It has a nearby notification attachment which I want to fetch using this method.
The beaconID in hex is 5dc33487f02e477d40580117c55e7d5f.
I referred to this guide for help but it seems the request they are making is wrong considering the namespacedTypes should be an array and it is a string in the blog.
Here is the documentation for the API.
UPDATE:
If I do a Proximity API list attachment call I get the following result for the same beacon
[
{
"data":"eyJ1cmwiOiAiaHR0cHM6Ly9xLmVkZHkucHJvLzhsMkl3SiIsICJkZXNjcmlwdGlvbiI6ICJTb21lIiwgInRpdGxlIjogIlNvbWUifQ==",
"creationTimeMs":"2017-12-01T18:15:37.418Z",
"attachmentName":"beacons/3!5dc33487f02e477d40580117c55e7d5f/attachments/58dad403-7a99-4085-b338-5fe0b6660abd",
"namespacedType":"com.google.nearby/en"
}
]
Does this mean there is something wrong with the beaconinfo:getforobserved API call?
My understanding is that getforobserved cannot fetch nearby notification attachments but only the attachments defined under the "Attachments" sections in Beacon Dashboard (consisting of a namespace, type and value). The documentation says that getforobserved accepts * to specify all types in all namespaces owned by the client. For nearby notification attachments the namespace is com.google.nearby which is not owned by client. This is my best understanding but I'm not 100% sure about it.
In any case, your getforobserved request looks correct to me. You can verify that the request works correctly by either:
1) Removing the "namespacedTypes" completely from the POST data. In this case the request will not return any attachments but it should return beacon info, so you should get a non-empty answer if the request is otherwise OK.
2) Add an attachment (the other type instead of nearby notification) to the beacon and see if the request returns something. The API will return empty if namespacedTypes is defined but there are no attachments.
Android devices can get nearby notifications automatically if they are enabled on the phone, so typically there should not be need to request the nearby attachments manually. If you want to maintain nearby notification attachments through the API, you can use the other methods provided in the API (such as the list method). If you want to scan for beacons and fetch attachments, I would use the normal attachments which offers more flexibility with the content.

How to notify specific people with yammer REST api

I would like to notify a specific set of people when posting a message with Yammer's REST API. The desired effect should be the same as "Add people to notify" in the native web application:
After some research with the REST API documentation, i found the direct_to_id field in the request data object.
direct_to_id - Send a private message directly to the user indicated.
I'm not sure what this attribute actually does, so I tried the following:
var data = {
"body": "test message",
"group_id": XXXXXX, //a valid group id
"direct_to_id": XXXXXXXXXX, //a valid user id
};
but after I add the "direct_to_id" field to my post, i get a 400 (bad request) error. I also don't know if this method works with notifications to multiple users.
Ok, i figured it out by reverse engineering the yammer embed widget. When posting a message with people to be notified, yammer embed set a "cc" field in the web form, according to fiddler:
In javascript, simply do this:
var data = {
"body": "test message",
"group_id": XXXXXX,
"cc": "[[user:XXXXXX]],[[user:XXXXXX]]",
};
This approach is not documented in Yammer's API so I'm not sure if it will be supported in the future. In the meanwhile, I really wish that Yammer had better documentation. It would save developers lots of time and trouble.

Where from does ComponentSpace Saml 2.0 take key to encrypt assertions

When I run the following code (ComponentSpace Saml 2.0 lib is used), Fiddler shows me that SAMLRequest's value is encrypted like this <input type="hidden" name="SAMLRequest" value="PHNhbWxwOkF1dGhu...."> which is pretty expected behavior. The code implements the first step of SSO SAML 2.0 POST profile. Note that no certificate key is specified in the code to do any kind of encryption, so I wonder how does ComponentSpace lib decide which one to pick up?
var authnRequest = new AuthnRequest
{
Destination = #"https://idpserver/...",
Issuer = new Issuer(#"https://sp/..."),
ForceAuthn = false,
NameIDPolicy = new NameIDPolicy(null, null, true),
ProtocolBinding = SAMLIdentifiers.BindingURIs.HTTPPost,
AssertionConsumerServiceURL = #"https://sp/..."
};
var relayState = RelayStateCache.Add(new RelayState(#"https://sp/...", null));
ServiceProvider.SendAuthnRequestByHTTPPost(
new HttpResponseWrapper(_context.Response),
#"https://idpserver/...",
authnRequest.ToXml(),
relayState);
All the Wikipedia says is "the value of the SAMLRequest parameter is the base64 encoding". No information about what the key is used to encode.
Sorry for misunderstanding your question. Your example code constructed and sent an authn request. It sounds like you're asking about SAML assertions contained in a SAML response.
The identity provider encrypts the SAML assertion using the service provider's public key. The service provider will decrypt the assertion using its private key.
If you'd like to see an example of this, please take a look at the AssertionExample project which demonstrates encrypting/decrypting SAML assertions.
Step 2 at the link you supplied describes the SP sending an AuthnRequest via HTTP/POST to the IdP. There is no XML encryption involved in sending an AuthnRequest. The XML is encoded using deflate and base-64 but no encryption. This encoding is done for you when you call ServiceProvider.SendAuthnRequestByHTTPPost.
Signing the authn request is optional.
To sign the request, before calling ServiceProvider.SendAuthnRequestByHTTPPost, you need to do something like the following:
// Serialize to XML
XmlElement authnRequestElement = authnRequest.ToXml();
// Sign the authn request
SAMLMessageSignature.Generate(authnRequestElement, x509Certificate.PrivateKey, x509Certificate);
// Send the authn request to the IdP
ServiceProvider.SendAuthnRequestByHTTPPost(..., authnRequestElement, ...);
You always sign with your private key and the recipient will verify the signature using your public key/certificate.

Resources