ZekeSnider / NintendoSwitchRESTAPI

Reverse engineered REST API used in the Nintendo Switch app for iOS. Includes documentation on Splatoon 2's API.
MIT License
643 stars 43 forks source link

Parental Controls #12

Open SolidTux opened 4 years ago

SolidTux commented 4 years ago

Would be great to have more information about the parental control API to get a detailed playtime per game. I have a Wireshark recording, but I'll first have to figure out how to strip sensitive information from that before posting.

frozenpandaman commented 4 years ago

This would be cool to see!

SolidTux commented 4 years ago

My current problem is the session_code_verifier for the /connect/1.0.0/api/session_token. It is not the state like with the normal Switch app. In fact it doesn't appear in any form in the initial URL or the redirection URL.

SolidTux commented 4 years ago

Ok, I can now login, it works like here, but the rest doesn't really work yet as you apparently need to set special "X-Moon-*" headers.

Currently, the backend returns null pointer exceptions :-D

SolidTux commented 4 years ago

Ok, I got it to run and made an example to print play time per game (see here). As said before, the authentication works as expected up to the token API. After that you have an access_token and an id_token.

First we have to get the user id with

GET /2.0.0/users/me HTTP/1.1
Authorization: Bearer access_token
Host: api.accounts.nintendo.com

HTTP/1.1 200 OK
{
  "nickname": "nickname",
  "id": "user_id",
  ...a lot of other stuff...
}

Now the next step is to register the device with the online service. From now on the following headers probably have to be present (I didn't test which you can remove, but the device id should probably be required):

Authorization: Bearer access_token
X-Moon-App-Id: com.nintendo.znma
X-Moon-Os: ANDROID
X-Moon-Os-Version: 28
X-Moon-Model: some model
X-Moon-TimeZone: Europe/Berlin
X-Moon-Os-Language: de-DE
X-Moon-App-Language: de-DE
X-Moon-App-Display-Version: 1.11.0
X-Moon-App-Internal-Version: 229
X-Moon-Smart-Device-Id: device_id
User-Agent: moon_ANDROID/1.11.0 (com.nintendo.znma; build:229; ANDROID 28)

device_id can be chosen. The device is registered using

POST /moon/v1/users/user_id/smart_devices HTTP/1.1
Host: api-lp1.pctl.srv.nintendo.net
{
  "appLanguage": "de-DE",
  "appVersion": {
    "displayedVersion": "1.11.0",
    "internalVersion": "229"
  },
  "bundleId": "com.nintendo.znma",
  "id": "device_id",
  "modelName": some model name,
  "os": "ANDROID",
  "osLanguage": "de-DE",
  "osVersion": "28",
  "timeZone": "Europe/Berlin"
}

HTTP/1.1 201 Created
{
  "id": device_id,
  "nintendoAccountId": account_id,
  "bundleId": "com.nintendo.znma",
  "os": "ANDROID",
  "osVersion": "28",
  "modelName": some model name,
  "timeZone": "Europe/Berlin",
  "appVersion": {
    "displayedVersion": "1.11.0",
    "internalVersion": 229
  },
  "osLanguage": "de-DE",
  "appLanguage": "de-DE",
  "notificationToken": null,
  "updateRequired": false,
  "createdAt": timestamp,
  "updatedAt": timestamp
}

The registration is only needed once. This allows us now to get the ids of the registered devices

GET /moon/v1/users/user_id/devices?filter.device.activated.$eq=true HTTP/1.1
Host: api-lp1.pctl.srv.nintendo.net

HTTP/1.1 200 OK
{
  "count": 1,
  "items": [
    {
      "deviceId": "device_id",
      "device": {
        "id": "device_id",
        ...more information...
      },
      ...more information...
    },
    ...
  ]
}

For each device we can get the daily summaries

GET /moon/v1/devices/device_id/daily_summaries HTTP/1.1
Host: api-lp1.pctl.srv.nintendo.net

HTTP/1.1 200 OK
{
  "count": 31,
  "items": [
    {
      "deviceId": "device_id",
      "date": "2020-04-06",
      "result": "CALCULATING" or "ARCHIEVED",
      "playingTime": seconds,
      "exceededTime": null,
      "disabledTime": 0,
      "miscTime": 0,
      "importantInfos": [],
      "notices": [],
      "observations": [],
      "playedApps": [
        {
          "applicationId": "application id",
          "title": "title",
          ...
        },
        ...
      ],
      "anonymousPlayer": null,
      "devicePlayers": [
        {
          "playerId": "player_id",
          "nickname": "nickname",
          "imageUri": "image",
          "playingTime": seconds,
          "playedApps": [
            {
              "applicationId": "app_id",
              "firstPlayDate": date,
              "playingTime": seconds
            },
            ...
          ]
        }
      ],
      "timeZoneUtcOffsetSeconds": 7200,
      "lastPlayedAt": time stamp,
      "createdAt": time stamp,
      "updatedAt": time stamp
    },
    ...
  ],
  "updatedRecently": true
}

or a list of available monthly summaries

GET /moon/v1/devices/device_id/monthly_summaries HTTP/1.1
Host: api-lp1.pctl.srv.nintendo.net

HTTP/1.1 200 OK
{
  "count": 11,
  "indexes": [
    "2020-03",
    ...
  ],
  "items": [
    {
      "deviceId": "device_id",
      "month": "2020-03"
    },
    ...
  ]
}

Each montly summary can be obtained with

GET /moon/v1/devices/device_id/monthly_summaries/2020-03 HTTP/1.1
Host: api-lp1.pctl.srv.nintendo.net

HTTP/1.1 200 OK
{
  "deviceId": "device_id",
  "month": "2020-03",
  "dailySummaries": {
    "2020-03-11": {
      "date": "2020-03-11",
      "result": "ACHIEVED",
      "playingTime": seconds
    },
    ...
  },
  "playingDays": 31,
  "playedApps": [
    {
      "applicationId": "application id",
      "title": "title",
      ...
    },
    ...
  ],
  "insights": {
    "thisMonth": {
      "averagePlayingTime": seconds,
      "playingDays": days,
      "playingTime": seconds
    },
    "previousMonth": {
      "averagePlayingTime": second,
      "playingDays": days,
      "playingTime": seconds
    },
    "rankings": {
      "byDay": [
        {
          "applicationId": "application id",
          "units": days,
          "position": "UP"/"DOWN"/"STAY",
          "ratio": ratio
        },
        ...
      ],
      "byTime": [
        {
          "applicationId": "application id",
          "units": seconds,
          "position": "UP"/"DOWN"/"STAY",
          "ratio": ratio
        },
        ...
      ]
    }
  },
  "devicePlayers": [
    {
      "playerId": "player_id",
      "nickname": "nickname",
      "imageUri": "image",
      "dailySummaries": {
        "2020-03-11": {
          "date": "2020-03-11",
          "result": "ACHIEVED",
          "playingTime": seconds
        },
        ...
      },
      "insights": {
        "thisMonth": {
          "averagePlayingTime": seconds,
          "playingDays": days,
          "playingTime": seconds
        },
        "previousMonth": {
          "averagePlayingTime": seconds,
          "playingDays": days,
          "playingTime": seconds
        },
        "rankings": {
          "byDay": [
            {
              "applicationId": "application id",
              "units": days,
              "position": "UP"/"DOWN"/"STAY",
              "ratio": ratio
            },
            ...
          ],
          "byTime": [
            {
              "applicationId": "application id",
              "units": seconds,
              "position": "UP"/"DOWN"/"STAY",
              "ratio": ratio
            },
            ...
          ]
        }
      }
    },
    ...
  ],
  "includedMajorVersions": [
    9
  ],
  "createdAt": timestamp,
  "updatedAt": timestamp
}

There are a lot or more endpoints, e.g. for the parental control setting, but I haven't looked at them.