============ Registration ============ Needed data ----------- To register a device to an UONET+ API account, you need this set of data: * token (provided by UONET+), e.g. ``OP1JW6K``, later: ``{token}`` * PIN (provided by UONET+), e.g. ``493425``, later: ``{pin}`` * symbol of school's administrative unit (borough [gmina] or county [powiat], depending on school type), e.g. ``opole``, ``powiatnyski`` (provided by UONET+; also see: `symbol generator by Wulkanowy project `_), later: ``{symbol}`` * `Firebase (Google) Cloud Messaging `_ token, later: ``{firebase-token}`` * device name (could be any). Getting the Firebase token ----------------------------- There are 2 ways to get the Firebase token: * Simulate Google app's requests manually, as in `erupcja/fcm-listener-node `_, to get the token directly from Google. Requires app's Firebase keys and other data (could be extracted from APK with `erupcja/apk-firebase-data-extractor `_). No way a code like this will pass in a Google Play app. * Hijack the token from the official app. The app can be manipulated to connect with your HTTP(S) server by either a QR code you can generate with `wulkanowy/qr `_ or by HTTP (no HTTPS!) MITM. Reference hijack implementation: `erupcja/vulcan-firebase-token-hijacker `_. Generating the certificate -------------------------- The app has to generate a X.509 RSA or ECDSA certificate, a different one for each user, to sign the API requests. - ``{cert-fingerprint}`` - SHA-1 hash of DER form of certificate - ``{cert-pem}`` - PEM form of the certificate without the headers (``-----BEGIN CERTIFICATE-----``, ``-----END CERTIFICATE-----``) - ``{cert-private-key}`` - PEM form of the PKCS#8 certificate's private key without the headers (``-----BEGIN PRIVATE KEY-----``, ``-----END PRIVATE KEY-----``) See the ready-to-use implementations: `erupcja/uonetplus-hebe-certificate-generator `_. Getting the endpoint -------------------- There are 2 ways for that: * decoding and parsing QR code * from a Vulcan's text file QR code content are encrypted by ``aes-128-ecb`` algorithm with password ``tDVS4ykCBBAeN33h``. See `github.com/wulkanowy/qr `_ for libraries. Example decoded content: ``CERT#https://uonetplus-komunikacja.eszkola.opolskie.pl/opole/mobile-api#OP1JW6K#ENDCERT`` Parsing regex (ECMAScript notation): ``/^CERT#([^#]+)#([A-Z0-9]{7})#ENDCERT$/`` ``mobile-api`` should be replaced with ``api/mobile`` by the app (for the sake of compatibility with ``pl.vulcan.uonetmobile``). This means that example ``{raw-endpoint}`` is ``https://uonetplus-komunikacja.eszkola.opolskie.pl/opole/api/mobile``. The no-QR method is to get the matching endpoint from file. Make a simple HTTP GET request to ``https://komponenty.vulcan.net.pl/UonetPlusMobile/RoutingRules.txt`` to obtain it. App should do this dynamically, as new UONET+ instances can be created by the time. Line ``OP1,https://uonetplus-komunikacja.eszkola.opolskie.pl`` means that tokens starting with ``OP1`` should use ``https://uonetplus-komunikacja.eszkola.opolskie.pl`` as the correct server. Now the ``{raw-endpoint}`` is ``{server}/{symbol}/api/mobile``, for example: ``https://uonetplus-komunikacja.eszkola.opolskie.pl/opole/api/mobile``. /register/new ----------------- Request: :: POST {raw-endpoint}/register/new HTTP/1.1 vOS: Android vDeviceModel: Redmi Note 3 vAPI: 1 vDate: Wed, 08 Apr 2020 20:49:11 GMT vCanonicalUrl: api%2fmobile%2fregister%2fnew Digest: SHA-256=EElE76CuYuIE08hH03kdewLfYr6x9YU8E8AOBue3yK4= Signature: keyId="{cert-fingerprint}",headers="vCanonicalUrl Digest vDate",algorithm="sha256withecdsa",signature=Base64(SHA256withECDSA(MEUCIG1VRVdOqPcjWcvRN5CtdrSjnoqAZqgiexTVWc5zA+9TAiEAneXzS4pk7HGy/u2IlvMRApKu6lOC1/6XUBv+GbKKO0U=)) Content-Type: application/json; charset=utf-8 Connection: Keep-Alive Accept-Encoding: gzip User-Agent: okhttp/3.11.0 { "API": 1, "AppName": "DzienniczekPlus 2.0", "AppVersion": "1.0", "CertificateId": "{cert-fingerprint}", "Envelope": { "Certificate": "{cert-pem}\n", "CertificateThumbprint": "{cert-fingerprint}", "CertificateType": "X509", "DeviceModel": "Xiaomi Redmi Note 3", "OS": "Android", "PIN": "{pin}", "SecurityToken": "{token}", "SelfIdentifier": "5a09095f-11a5-4582-9906-a871c5e0814f" }, "FirebaseToken": "{firebase-token}", "RequestId": "f94790a5-9c7e-4956-b913-430a670ce8a7", "Timestamp": 1586378950907, "TimestampFormatted": "2020-04-08 22:49:11" } Response: :: HTTP/1.1 200 OK Content-Length: 359 Content-Type: application/json; charset=utf-8 Expires: -1 Server: Microsoft-IIS/7.5 Set-Cookie: ARR_uonetplus-komunikacja.eszkola.opolskie.pl=8a141ee1938645c8e894da9b020145905e68ccfdfd56e212f2bb21bfd8f761de;Path=/;Domain=uonetplus-komunikacja.eszkola.opolskie.pl X-AspNet-Version: 4.0.30319 X-Powered-By: ARR/2.5 X-Powered-By: ASP.NET Date: Wed, 08 Apr 2020 20:49:13 GMT { "Envelope": { "LoginId": 177013, "RestURL": "https://uonetplus-komunikacja.eszkola.opolskie.pl/opole/", "UserLogin": "laura@erupcja.science", "UserName": "laura@erupcja.science" }, "EnvelopeType": "AccountPayload", "InResponseTo": null, "RequestId": "b26023b0-d547-495c-b79e-5336900ef107", "Status": { "Code": 0, "Message": "OK" }, "Timestamp": 1586378954618, "TimestampFormatted": "2020-04-08 22:49:14" } /register/hebe -------------- Request: :: GET {raw-endpoint}/register/hebe HTTP/1.1 vOS: Android vDeviceModel: Redmi Note 3 vAPI: 1 vDate: Wed, 08 Apr 2020 20:49:14 GMT vCanonicalUrl: api%2fmobile%2fregister%2fhebe Signature: keyId="{cert-fingerprint}",headers="vCanonicalUrl vDate",algorithm="sha256withecdsa",signature=Base64(SHA256withECDSA(MEUCIQDDqLOuUrlzi7VWTO0gOM8Cr3wKcTzirMV9xQJqcwFkQAIgdlizXw53XCdIiX8X9xO3mKScoXj4+lFz1a8XJg8phy8=)) Content-Type: application/json; charset=utf-8 Connection: Keep-Alive Accept-Encoding: gzip User-Agent: okhttp/3.11.0 Response: :: { "Envelope": [ { "Capabilities": [ "REGULAR", "TOPICS_ENABLED", "ADDRESS_BOOK_PUPIL" ], "ClassDisplay": "2et", "ConstituentUnit": { "Address": "ul.Tadeusza Kościuszki 39-41, 45-062 Opole", "Id": 3, "Name": "Publiczne Technikum Nr 5 w Zespole Szkół Elektrycznych im. Tadeusza Kościuszki w Opolu", "Patron": null, "SchoolTopic": "bab6ff93-23b6-e411-9b68-005056b435d0", "Short": "PT5" }, "Educators": [ { "Id": "e-234", "Initials": "MK", "LoginId": 4727, "Name": "Kurisu", "Roles": [ { "Address": "Makise Kurisu [MK] - wychowawca 2et (PT5)", "AddressHash": "7042b84fe7da034d62251907fdae6c2d2e3373f0", "ClassSymbol": "2et (PT5)", "ConstituentUnitSymbol": "PT5", "Initials": "MK", "Name": "Kurisu", "RoleName": "Wychowawca", "RoleOrder": 0, "Surname": "Makise", "UnitSymbol": null } ], "Surname": "Makise" } ], "FullSync": false, "InfoDisplay": "OpoleZSE - et2", "Journal": { "Id": 796, "YearEnd": { "Date": "2020-08-31", "DateDisplay": "31.08.2020", "Time": "00:00:00", "Timestamp": 1598824800000 }, "YearStart": { "Date": "2019-09-01", "DateDisplay": "01.09.2019", "Time": "00:00:00", "Timestamp": 1567288800000 } }, "Login": { "DisplayName": "Laura Karaś", "FirstName": "Laura", "Id": 177013, "LoginRole": "Uczen", "SecondName": "Lena", "Surname": "Karaś", "Value": "laura@erupcja.science" }, "Partition": "opole-OpoleZSE", "Periods": [ { "Current": false, "End": { "Date": "2019-01-01", "DateDisplay": "01.01.2019", "Time": "00:00:00", "Timestamp": 1546297200000 }, "Id": 1725, "Last": false, "Level": 1, "Number": 1, "Start": { "Date": "2018-09-01", "DateDisplay": "01.09.2018", "Time": "00:00:00", "Timestamp": 1535752800000 } }, { "Current": false, "End": { "Date": "2019-08-31", "DateDisplay": "31.08.2019", "Time": "00:00:00", "Timestamp": 1567202400000 }, "Id": 1726, "Last": true, "Level": 1, "Number": 2, "Start": { "Date": "2019-01-02", "DateDisplay": "02.01.2019", "Time": "00:00:00", "Timestamp": 1546383600000 } }, { "Current": false, "End": { "Date": "2020-01-01", "DateDisplay": "01.01.2020", "Time": "00:00:00", "Timestamp": 1577833200000 }, "Id": 1727, "Last": false, "Level": 2, "Number": 1, "Start": { "Date": "2019-09-01", "DateDisplay": "01.09.2019", "Time": "00:00:00", "Timestamp": 1567288800000 } }, { "Current": true, "End": { "Date": "2020-08-31", "DateDisplay": "31.08.2020", "Time": "00:00:00", "Timestamp": 1598824800000 }, "Id": 1728, "Last": true, "Level": 2, "Number": 2, "Start": { "Date": "2020-01-02", "DateDisplay": "02.01.2020", "Time": "00:00:00", "Timestamp": 1577919600000 } } ], "Pupil": { "FirstName": "Laura", "Id": 2137, "LoginId": 177013, "LoginValue": "laura@erupcja.science", "SecondName": "Lena", "Sex": false, "Surname": "Karaś" }, "SenderEntry": { "Address": "Karaś Laura - uczennica 2et (PT5)", "AddressHash": "5dfca102c59680cf9a16a629157cee630f56304f", "Initials": "KL", "LoginId": 177013 }, "TopLevelPartition": "opole", "Unit": { "Address": "ul.Tadeusza Kościuszki 39-41, 45-062 Opole", "DisplayName": "Zespół Szkół Elektrycznych im.Tadeusza Kościuszki", "Id": 3, "Name": "Zespół Szkół Elektrycznych ", "Patron": "Tadeusz Kościuszko", "RestURL": "https://uonetplus-komunikacja.eszkola.opolskie.pl/opole/OpoleZSE/api", "Short": "OpoleZSE", "Symbol": "OpoleZSE" } } ], "EnvelopeType": "IEnumerable`1", "InResponseTo": null, "RequestId": "b860931a-ae3e-4939-a27f-adcfbbf95143", "Status": { "Code": 0, "Message": "OK" }, "Timestamp": 1586378956901, "TimestampFormatted": "2020-04-08 22:49:16" } What's next? ------------ Next requests should be sent to ``{unit-endpoint}``, which is ``body.Envelope[*].Unit.RestURL`` + ``/mobile``.