LINEWORKS API 2.0 C# で AccessToken を得る方法 [Newtonsoft.Json を使わない版]
NET 5.0 ~ は、「ImportFromPem」というメソッドが備わっているため、LINEWORKS が提供している PEM 形式の private-key をそのまま読み込める。
System.Collections.Generic.Dictionary<string, string> GetAccessToken(System.IO.FileInfo privateKey, string service_account, string client_id, string client_secret, params string[] scopes) { using (var rsa = System.Security.Cryptography.RSA.Create()) { rsa.ImportFromPem(System.IO.File.ReadAllText(privateKey.FullName)); var descriptor = new Microsoft.IdentityModel.Tokens.SecurityTokenDescriptor { Issuer = client_id, Claims = new System.Collections.Generic.Dictionary<string, object>() { ["sub"] = service_account }, SigningCredentials = new Microsoft.IdentityModel.Tokens.SigningCredentials(new Microsoft.IdentityModel.Tokens.RsaSecurityKey(rsa), "RS256"), IssuedAt = System.DateTime.UtcNow, Expires = System.DateTime.UtcNow.AddMinutes(60), }; var handler = new System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler(); var pv = new System.Collections.Specialized.NameValueCollection() { ["assertion"] = handler.WriteToken(handler.CreateJwtSecurityToken(descriptor)), ["grant_type"] = @"urn:ietf:params:oauth:grant-type:jwt-bearer", ["client_id"] = client_id, ["client_secret"] = client_secret, ["scope"] = string.Join(",", scopes), }; using (var wc = new System.Net.WebClient()) { var resText = System.Text.Encoding.ASCII.GetString(wc.UploadValues(@"https://auth.worksmobile.com/oauth2/v2.0/token", pv)); return System.Text.Json.JsonSerializer.Deserialize<System.Collections.Generic.Dictionary<string, string>>(resText); } } }
※「System.IdentityModel.Tokens.Jwt」を nuget しておくこと
NET Framework ~ 4.8 だと XML 形式の private-key しか読めないので、いったん NET 5.0 ~ を使って XML に変換するプログラムを書いて変換しておく。
void PEM2XML(System.IO.FileInfo pemFile) { using (var rsa = System.Security.Cryptography.RSA.Create()) { rsa.ImportFromPem(File.ReadAllText(pemFile.FullName)); File.WriteAllText(pemFile.FullName + ".xml", rsa.ToXmlString(true)); } }
そのうえで、「ImportFromPem」の代わりに「FromXmlString」を使う。
System.Collections.Generic.Dictionary<string, string> GetAccessToken(System.IO.FileInfo privateKey, string service_account, string client_id, string client_secret, params string[] scopes) { using (var rsa = System.Security.Cryptography.RSA.Create()) { rsa.FromXmlString(System.IO.File.ReadAllText(privateKey.FullName)); var descriptor = new Microsoft.IdentityModel.Tokens.SecurityTokenDescriptor { Issuer = client_id, Claims = new System.Collections.Generic.Dictionary<string, object>() { ["sub"] = service_account }, SigningCredentials = new Microsoft.IdentityModel.Tokens.SigningCredentials(new Microsoft.IdentityModel.Tokens.RsaSecurityKey(rsa), "RS256"), IssuedAt = System.DateTime.UtcNow, Expires = System.DateTime.UtcNow.AddMinutes(60), }; var handler = new System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler(); var pv = new System.Collections.Specialized.NameValueCollection() { ["assertion"] = handler.WriteToken(handler.CreateJwtSecurityToken(descriptor)), ["grant_type"] = @"urn:ietf:params:oauth:grant-type:jwt-bearer", ["client_id"] = client_id, ["client_secret"] = client_secret, ["scope"] = string.Join(",", scopes), }; using (var wc = new System.Net.WebClient()) { var resText = System.Text.Encoding.ASCII.GetString(wc.UploadValues(@"https://auth.worksmobile.com/oauth2/v2.0/token", pv)); return System.Text.Json.JsonSerializer.Deserialize<System.Collections.Generic.Dictionary<string, string>>(resText); } } }
M5Stack CAT-M Unit にpovo を挿し、インターネットから画像をダウンロードし画面表示/印刷
ボードマネージャの選択が M5Stack ATOM だったらプリンターに、それ以外だったら液晶に、それぞれ出力するサンプルです。
どちらも LovyanGFX を用いますが、プリンターの場合は 2021/12/31 時点では developブランチ が必要です。
また、プリンターの場合は更に拙作の LGFX_PrinterAddon も必要です。
その他 TinyGSM と ArduinoHttpClient も利用しています。
/* https://twitter.com/wakwak_koba/ */ #define TINY_GSM_MODEM_SIM7080 #define TINY_GSM_RX_BUFFER 1500 #define TINY_GSM_YIELD() { delay(1); } #include <TinyGsmClient.h> #include <ArduinoHttpClient.h> # define apn "povo.jp" # define server "t.wakwak-koba.jp" # define port 80 # define url "/garakuta/monodora.png" TinyGsm modem(Serial1); TinyGsmClient client(modem); # define LGFX_USE_V1 # define LGFX_AUTODETECT #include <LovyanGFX.hpp> # if defined(ARDUINO_M5Stack_ATOM) #include <SerialPrinter.hpp> #include <printer/aiebcy/EM5820.hpp> static lgfx_addon::SerialPrinter<lgfx_addon::aiebcy::EM5820> output(&Serial2, 192, 19); # else static LGFX output; # endif void setup() { Serial.begin(115200); # if defined(ARDUINO_M5Stack_ATOM) Serial1.begin(9600, SERIAL_8N1, 32, 26); // CAT-M Unit Serial2.begin(9600, SERIAL_8N1, 33, 23); // ATOM Printer # else Serial1.begin(9600, SERIAL_8N1, 22, 21); // CAT-M Unit # endif delay(5000); Serial.println("restart modem"); modem.restart(); Serial.println("wait for network connected "); //modem.gprsConnect(apn, "", ""); // 初回だけ必要かも if (modem.waitForNetwork() && modem.isNetworkConnected()) { Serial.print("connecting to "); Serial.println(apn); modem.gprsConnect(apn, "", ""); if(modem.isGprsConnected()) { Serial.print("connecting to "); Serial.println(server); auto httpClient = HttpClient(client, server, port); Serial.print("get "); Serial.println(url); httpClient.get(url); auto statusCode = httpClient.responseStatusCode(); auto contentLength = httpClient.contentLength(); Serial.printf("StatusCode:%d", statusCode); Serial.println(); Serial.printf("contentLength:%d", contentLength); Serial.println(); output.init(); output.clear(TFT_WHITE); uint8_t* buf = new uint8_t[contentLength]; if(buf != nullptr) { httpClient.read(buf, contentLength); output.drawPng(buf, contentLength); delete []buf; } else { lgfx::StreamWrapper stream; stream.set(&httpClient, contentLength); output.drawPng(&stream); } output.setTextColor(TFT_BLACK); output.setFont(&fonts::Font4); output.setTextDatum(textdatum_t::top_right); output.drawString("HELLO", output.width() - 1, 0); output.drawString("DORAEMON", output.width() - 1, 30); # if defined(ARDUINO_M5Stack_ATOM) output.display(); # endif httpClient.stop(); modem.gprsDisconnect(); } } Serial.println("disconnected"); } void loop() { delay(1); }