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);
        }
    }
}