google / jwt_verify_lib Goto Github PK
View Code? Open in Web Editor NEWProvide c++ library to verify JWT tokens
License: Apache License 2.0
Provide c++ library to verify JWT tokens
License: Apache License 2.0
This issue is cross posted on istio and envoy: istio/istio#19424 and envoyproxy/envoy#9426
The following JWKS from PingFederate causes a parse error. Note that PingFederate is a very common enterprise IdP/SSO, and this error breaks envoy's ability to do OIDC token verification. Lastly, according to the RFC for JWK the "alg" field is optional, and this library shouldn't assuming that the algorithm is "ES256" if missing.
{"keys":[{"kty":"EC","kid":"Ozxgl1WsWyQBF1lcbYyjYl","use":"sig","x":"Vq3vKJCeJyihZIauouQp3eFmuYLCsEcID_sGigAx2gs","y":"3UaPoda-HCrJeU4i960qFHMschTdmqjkitiIyYc5svA","crv":"P-256"},{"kty":"EC","kid":"RBsu7DAEUcrgx4X91TVrsY","use":"sig","x":"dQ_SlIctw4nWZEJ2rasiyrps7jXuxr1E81zw4X-aaY1LLRhcaUxpInytXeZK5mOm","y":"xrhQlZQZeDWm7VRHThevLTKqOkqfNwTmHL7P_f23BPn8SLlXd9p1jS4LzL0KK0rL","crv":"P-384"},{"kty":"RSA","kid":"X3oArm2sGh5pO0jWl5O41C","use":"sig","n":"lOTXC2Pfi0dajzDAOERQLTtT0_GbjAFaNflzV-0tWxban1CPEN0n5UG5z-c0KxKY6fhZshQ1Opr3VbQmE1MGSeYf3qEUD4Th3ZubVV_2Yhuio-UZXllz1EGgAh7sC9TzQi84jIYj_mhdno4l6Y3FVlvM6VtNYRGPDNgjRoVfd63vocXAqgUok6LpEcL9MbrvmK-hFPVNX7euGN_xm_qZM5-JrJMnKz6shnjrul7yZ-ZExzfFx_LSBqum-fkKv2FfEoJqyzyVlbabmDUZ81B9ZP0nfaP3e-IRSQECuXf52PfHqEgZbrax8hKAfpZKJX643gxSCnBWdmNO-BFSlho4pw","e":"AQAB"},{"kty":"EC","kid":"sxG_WeuLxIKXoVit-8vyQf","use":"sig","x":"AG3w2vYgVbn4E27rkxZPUVrzLWhMctY5GOP6xygLLFwNRaoOx2gnlQPwAsEXHxz80u5lfmOms0pJSjuDrNqs5pB4","y":"Ad0K-hbFmTVj3nMOw7jAdl21dlU35pG1g7h_Tswr0VYfxqg4ubIPyXrrtmlKH8q3c2Gqgq77Uq12qfcDE8zF2a4v","crv":"P-521"},{"kty":"EC","kid":"7uLnfLOhXPmOZ2BUaeUue-","use":"sig","x":"fR71QOze0q-0uDBuOyTdJANOA0Kz_WT0ykuuuLH94uCJzD13B5OBB1y4jtOvIYvX","y":"WwjH9z9kzSsGZmkl5BvXhyCO8udP9nNFuZM4WugT7C-kD0UNqojpCEkigWorRlin","crv":"P-384"},{"kty":"EC","kid":"FWwgHZozeidVHog8YEjhT-","use":"sig","x":"C2U_Vuv_t3VEOv-UaprJTir3L_SWq_gjwVSUcLTq5Bc","y":"U9F6c5qlnRLSR_KRiCyAzRk7YUpXrXbqsUJYYlTPw7A","crv":"P-256"},{"kty":"RSA","kid":"aNspNiifg5EiN-R1R-RO4O","use":"sig","n":"klTzMXRM83AWKoKRZQwI5XOdJ0tB0jr1ieo_uLT_W6w_hNY8jbFzN1xwxjyUZFBHQVDU2PlyjRZDKQ5tIRs05__DwCvYdDiN0i5f5ChpB66UkAUn1IuuGUQYP7OHVSdeDCFCq3q8UGrl2FCFRxyL9k9hu0VX7Pj06SNGr74_hbCXF6VJmcgDmoXrTXEC5hSBciauGmAxlIbhDbDx-3QHrPJ5P4OLCwH0kfW2RB50O6DMZmKhXeSqE0WiI9KbMz_Mq-TIMiPNkRC1Wsdv4sKoYHf1t6sdp6n9qconQRGwGZNaWcKk6nno-26a_8CxGAuHh8BIFIvFj5AohFFXu2-slw","e":"AQAB"},{"kty":"EC","kid":"ikc6qm-M_topQoGzydo8zI","use":"sig","x":"AE8C5SEixbzl9ez1NPsYOehpnPohIAMxucjZjw2E9aVCBdGo7t2hKv2Aa7ql4zpeTucrDP3ZyUKK6-D3m3C10ojx","y":"AXNjApqwEKFjFmR4bbxAf_8nS74zDuOtXsq1PnoU58ZcHON1ZBUOEXY0Y6IPw1Q8ngaLHg30I300L1ZL0aIX4ygv","crv":"P-521"},{"kty":"EC","kid":"403dlMPeSjSNp4cQ0GBSLL","use":"sig","x":"AI4FHJCcr7fMCb2BfCj9l6bptD513AVVSbOLP2wPM0Aq6CKOEWlN5Yc8qW1Z8NFOPkjeBsCagV02qNJFxH0oi9em","y":"AK3nxsAk3aMOwqIGU-9xXW2I_wwkRxbTR5cPPceWEzpvUHXqyfOXihVYPTJkbaYggyOWjlpw5RYJZGBTLkFUzdal","crv":"P-521"},{"kty":"EC","kid":"IcGz3uUD_EfbMQWQ-6SrmT","use":"sig","x":"10bDbsUH92XHCizkNtBzumyzro8aZypDTqG6ob1fMXk","y":"zfradNJdJ739STTO9vQmOtMl3r7XlnX2SNEusGsFtz0","crv":"P-256"},{"kty":"EC","kid":"oTrCYqttZBLTIv4R5lrfiZ","use":"sig","x":"kOxZSdBd2DPGjuf0lrV30Bc8LCj2EBMfmluPc3sV44fnsZWtTnQz4pCcDjz2hzOY","y":"587zK_ggKmzpYaKz8AaBKERnAsF0AfLjIo2dAu0BdCO0FzSJGyV_cDpNmAdx8ah4","crv":"P-384"},{"kty":"RSA","kid":"vty_MATEPV9warjejj_hef","use":"sig","n":"6TDzjPXHfjDygJ3wa-0BB4m028hatl-PnfT5BEFcIkkWhGRswQSpiGCGjb3DXQ4LxAwZ_XM5RuBYMOMZw9qEU01lhQycqYVOub7R0lli2oDETW4pOATa6JW7QyyXcbbcnYxqj6qfwKb_XfXBDfLpwT8K1_ylJxAymV1ZgfeXDexGBr7d4fLNzgGV7CjZcYmMftn3CktKrA2vy8fLQ2wfVQgfo1J9UqCmLoo6sorW2Sn23Vsx4sTN8OkcrYzpd8_0Gj0X4jXsnk05rfvAWfn8iBHxe6ERBuVh48SWkGjpRnLLoyFmZFg0d1aDlskWp2rYu0VeeUAfKDAeGAU3_1ihuQ","e":"AQAB"}]}
The relevant logs in envoy/istio-proxy are:
[Envoy (Epoch 0)] ...[warning][config] [external/envoy/source/common/config/grpc_mux_subscription_impl.cc:82] gRPC config for type.googleapis.com/envoy.api.v2.Listener rejected: Error adding/updating listener(s) 10.110.64.204_8443: Issuer 'https://pingfed.mycorp.io' in jwt_authn config has invalid local jwks: [crv] field specified is not compatible with [alg] for an EC key, virtualInbound: Issuer 'https://pingfed.mycorp.io:' in jwt_authn config has invalid local jwks: [crv] field specified is not compatible with [alg] for an EC key
and
[warning][filter] [./src/envoy/http/jwt_auth/pubkey_cache.h:85] Invalid inline jwks for issuer: https://pingfed.mycorp.io, jwks: REDACTED , error: JWK_EC_PUBKEY_PARSE_ERROR
When g++
compiles the code, it produces the following warning message.
external/com_github_google_jwt_verify/src/status.cc: In function 'std::string google::jwt_verify::getStatusString(google::jwt_verify::Status)':
external/com_github_google_jwt_verify/src/status.cc:182:1: warning: control reaches end of non-void function [-Wreturn-type]
182 | }
| ^
It seems that return ""
was dropped by recent change - ae1bb05
While clang does not produce the warning when all enum is covered by switch, GNU GCC complains about it. So I initially thought that it is a gcc issue but it seems gcc is correct and they have the FAQ - https://gcc.gnu.org/wiki/VerboseDiagnostics#enum_switch
env: g++ (GCC) 11.3.1 20220421
I have a requirement for boolean JWT Claim and I found that there is not support for boolean JWT Claim in this library ? Any particular reason to not support it.
As raised in #49 (comment)
If a JWKS has multiple JWK, now, it will just fail the JWKS parsing if one of them is not supported.
This will have problem with new alg added in the JWKS server. It is better to ignore these new JWK, and keep searching for the one it known.
As raised in istio/istio#41729
If a JWK has an exponent different from 65537 or 3, the parsing fails with the following error:
"Jwks RSA [n] or [e] field is missing or has a parse error"
For example, parsing will fail for the following valid JWK:
{
"keys": [
{
"n": "q9hO0xKd6KfjXRxzAzLYnV-o4wm7aRMSr27ajF2bDedVFMJ-G6Y01lRsSn0zTusBCbax10dEOMg1g_GEWWqG_hYYq2eQ0RAWHAEfUUZHacu1MrGUFFcmbRYlR_Q1lM-e-831yFFbDPZAIUpyN_8qZuVL1fN2cGRXNcAyVzrDqu2h1pdjQykyMGPKr9X3xxMZ-IHDhbnZjdeYBHJPvwic2G-_Wp1ZrHxBhUykZuAVS3RMFDH-pV6-_W0rOENv3tZmxBTTSGIGNHYGaMg_qrnVDZuNOv3XGv_McqlD0RFifqtCO5QQtCQAdwMXHxmv2SxC04LsFL3GgSh_G3otGhwItQ",
"e": "Iw",
"kty": "RSA",
"alg": "RS256",
"kid": "1u-zN2fkExpEMumSNIYBfV8XPjFPHalL8IM6mO285yE"
}
]
}
I couldn't find any reference that the exponent must be 65537 or 3, even though 65537 is commonly used.
opened PR #95, which should fix the JWK validation. I'd appreciate if you could review it.
Our identity provider (forgerock am ) delivers IMHO a valid jwks:
{
"kty": "RSA",
"kid": "....",
"use": "sig",
"x5t": "....",
"x5c": [
"......."
],
"n": ....",
"e": "AQAB",
"alg": "PS512"
},
(https://www.rfc-editor.org/rfc/rfc7518.html#section-3.3)
Expected result: The key is read successfully by the istio envoy.
Actual result: envoy complains about:
[alg] is not started with [RS] for a RSA key
This is referred in this istio issue: istio/istio#22597
The BoringSSL revision hasn't been updated since 2020. It seems this project tries to track Envoy, so probably should update it to match.
https://github.com/envoyproxy/envoy/blob/f4812764c43df5872fd773b25d399f7ff8fa4403/bazel/repository_locations.bzl#L113
Note BoringSSL now requires C++14 (consistent with https://github.com/google/oss-policies-info/blob/main/foundational-cxx-support-matrix.md, for reference Envoy requires C++17), so you may need to update your build. I think it's just a matter of adding build --cxxopt="--std=c++14"
to .bazelrc
, but I don't really understand Bazel, so that might not be sufficient.
Issue cross-posted to envoy: envoyproxy/envoy#10222
JWS Token failing to parse:
eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJwam9oV01GRVV3SmRCOHkwVXFpX3lWNEdFS3FIZUxpaUdZNGR2MUpIZzNvIn0.eyJqdGkiOiIwNjBhNzA2MS04ZGM0LTQ5Y2YtOGM5Zi05YjRjNjNjOTI2YTAiLCJleHAiOjE1ODMwOTkwMjYsIm5iZiI6MCwiaWF0IjoxNTgzMDk4NzI2LCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjgwODAvYXV0aC9yZWFsbXMvdGVzdCIsImF1ZCI6ImFjY291bnQiLCJzdWIiOiI1NWFiYjUzNi1jYWQyLTRhN2YtOGM3My05YzRlYTBjNDQ2ODYiLCJ0eXAiOiJCZWFyZXIiLCJhenAiOiJ0ZXN0IiwiYXV0aF90aW1lIjowLCJzZXNzaW9uX3N0YXRlIjoiZDMwY2U3ZmMtZDlmNi00NGQxLWE4YzktMDRmOTA5ZTgyMmYzIiwiYWNyIjoiMSIsInJlYWxtX2FjY2VzcyI6eyJyb2xlcyI6WyJvZmZsaW5lX2FjY2VzcyIsInVtYV9hdXRob3JpemF0aW9uIl19LCJyZXNvdXJjZV9hY2Nlc3MiOnsiYWNjb3VudCI6eyJyb2xlcyI6WyJtYW5hZ2UtYWNjb3VudCIsIm1hbmFnZS1hY2NvdW50LWxpbmtzIiwidmlldy1wcm9maWxlIl19fSwic2NvcGUiOiJwcm9maWxlIGVtYWlsIiwiY2xpZW50SWQiOiJ0ZXN0IiwiZW1haWxfdmVyaWZpZWQiOmZhbHNlLCJjbGllbnRIb3N0IjoiMTcyLjI1LjAuMSIsInByZWZlcnJlZF91c2VybmFtZSI6InNlcnZpY2UtYWNjb3VudC10ZXN0IiwiY2xpZW50QWRkcmVzcyI6IjE3Mi4yNS4wLjEifQ.ANh4Zxhw9OBO-3yrctnSTkXxf6Tc-jTw3kl-fVw2GF1ridxQ5HOFfbYa8elCtuyh75CYd4Q4b56bam4S424BOmwBY2nrqBr_oRHmxGTytzpIh0EdE2rJl_twPjQnqiyZzIO_sEHxkldt9vlSFxNUUHyyj2gJKLx8jaRCayPVIXQ44RZ1UWEP48Mhg5WDd6UzT9GxZNSsj6yxzDHzpNQLRVnsDEfJVEcHm2kftXP-O2LZ_TtH3LliLXoCBNAycfhfA4w0RlQW-aKZm-BtHV_PyqcvaeYeA2Gxn6aFNIBJx2ovWP-6Nx8U1k-6bFoXmmoZjQ_oZHtg1FDs1Yxc3XTj1A
Log Output:
[Envoy (Epoch 0)] [2020-03-01 21:38:58.383][24][debug][jwt] [external/envoy/source/extensions/filters/http/jwt_authn/authenticator.cc:107] JWT authentication starts (allow_failed=true) [Envoy (Epoch 0)] [2020-03-01 21:38:58.383][24][debug][jwt] [external/envoy/source/extensions/filters/http/jwt_authn/authenticator.cc:245] JWT token verification completed with: Jwt header is an invalid Base64url input or an invalid JSON [Envoy (Epoch 0)] [2020-03-01 21:38:58.383][24][debug][jwt] [external/envoy/source/extensions/filters/http/jwt_authn/filter.cc:61] Called Filter : check complete OK
Decoded Header:
{"alg":"RS256","typ" : "JWT","kid" : "pjohWMFEUwJdB8y0Uqi_yV4GEKqHeLiiGY4dv1JHg3o"}
Decoded Payload:
{"jti":"060a7061-8dc4-49cf-8c9f-9b4c63c926a0","exp":1583099026,"nbf":0,"iat":1583098726,"iss":"http://localhost:8080/auth/realms/test","aud":"account","sub":"55abb536-cad2-4a7f-8c73-9c4ea0c44686","typ":"Bearer","azp":"test","auth_time":0,"session_state":"d30ce7fc-d9f6-44d1-a8c9-04f909e822f3","acr":"1","realm_access":{"roles":["offline_access","uma_authorization"]},"resource_access":{"account":{"roles":["manage-account","manage-account-links","view-profile"]}},"scope":"profile email","clientId":"test","email_verified":false,"clientHost":"172.25.0.1","preferred_username":"service-account-test","clientAddress":"172.25.0.1"}
The Token can be correctly parsed by https://jwt.io/ which outputs the correct JSON. The header and payload parts can also be properly parsed by https://www.base64decode.org/, also outputting correct JSON.
This issue appears using the Envoy proxy (1.12.0) deployed with Istio 1.4.5 (current version). The Token is issued by a Keycloak 8.0.1 instance.
Hi,
Spent a lot of time finding out that JSON with the same attributes twice or more will throw an error JwtPayloadParseErrorBadJson
In my case, sub
was twice in JWT token.
Decoding of such JSON
{
"sub": "ba4c8db6-ec65-483a-8436-691dcd311a2c",
"someotherattribute": 123,
"sub": "[email protected]"
}
will return an error here: https://github.com/google/jwt_verify_lib/blob/master/src/jwt.cc#L98-L100
Most tools like jwt.io
, JSON.parse
, jq
and more will just produce an object with {"sub":"[email protected]","someotherattribute":123}
e.g. sub
will be just last value
I don't think that is so critical error for throwing exceptions.
Hi 👋
I'm seeing an issue on Istio 1.4.6
with the following error in jwt_authn config has invalid local jwks: Jwks is an invalid JSON, virtualInbound: Issuer 'xxxxxxxx' in jwt_authn config has invalid local jwks: Jwks is an invalid JSON
. I've narrowed down the source of the error message to be https://github.com/google/jwt_verify_lib/blob/master/src/jwks.cc#L429.
The keys are valid JSON and have this form
{"keys":[{"kty":"RSA","alg":"RS256","kid”:”xxx”,”use":"sig”,”e”:”xxxx”,”n”:”xxxxxxxxxx"}]}
I'm sure the error comes from Istio or Envoy side but wondering if anyone here might have context on this! Any help is appreciated.
You can see the thread in the Istio Slack
In the current implementation, jwks without kid can be applied to the jwt with kid.
Does this as expected?
As part of envoyproxy / istio changes, I am adding JWE support to this library and would like to upstream the changes. I am volunteering to get this feature added. Please advise if this feature request is acceptable.
JWE Asymmetric Algorithms (alg)
JWE Symmetric Encryptions (enc)
References
BASE64URL(UTF8(JWE Protected Header)) + '.' +
BASE64URL(JWE Encrypted CEKey) + '.' +
BASE64URL(JWE Initialization Vector) + '.' +
BASE64URL(JWE Ciphertext) + '.' +
BASE64URL(JWE Authentication Tag)
Hi,
JWT with a very small negative value in "exp" are classifies as valid.
Example:
{
"nbf": 1663663666,
"exp": -386380800,
"iss": "issuer",
"aud": "audience"
}
"iat", "nbf" and "exp" are parsed into a uint64_t type variable by the "GetInt64" function, not verifying they are positive numbers:
if (payload_getter.GetInt64("iat", &iat_) == StructUtils::WRONG_TYPE) {
return Status::JwtPayloadParseErrorIatNotInteger;
}
if (payload_getter.GetInt64("nbf", &nbf_) == StructUtils::WRONG_TYPE) {
return Status::JwtPayloadParseErrorNbfNotInteger;
}
if (payload_getter.GetInt64("exp", &exp_) == StructUtils::WRONG_TYPE) {
return Status::JwtPayloadParseErrorExpNotInteger;
}
StructUtils::FindResult StructUtils::GetInt64(const std::string& name,
uint64_t* value) {
const auto& fields = struct_pb_.fields();
const auto it = fields.find(name);
if (it == fields.end()) {
return MISSING;
}
if (it->second.kind_case() != google::protobuf::Value::kNumberValue) {
return WRONG_TYPE;
}
*value = static_cast<uint64_t>(it->second.number_value());
return OK;
}
In case of a negative number, a large number is stored in "value" and the JWT is classified as not expired:
Status Jwt::verifyTimeConstraint(uint64_t now, uint64_t clock_skew) const {
// Check Jwt is active (nbf).
if (now + clock_skew < nbf_) {
return Status::JwtNotYetValid;
}
// Check JWT has not expired (exp).
if (exp_ && now > exp_ + clock_skew) {
return Status::JwtExpired;
}
return Status::Ok;
}
Now we will try to iterate all the jwks to find a matched jwk and then verify it. Can we create a hash table based on the kid
? And if kid
of jwt is not empty, we can try a quick search first before the iteration?
Status verifyJwtWithoutTimeChecking(const Jwt& jwt, const Jwks& jwks) {
// Verify signature
std::string signed_data =
jwt.header_str_base64url_ + '.' + jwt.payload_str_base64url_;[Wayne Zhang, 4 years ago: • Initial code](https://sourcegraph.com/github.com/google/jwt_verify_lib/-/commit/d856e7692ce470a961ac15d635570ac5379036f3)
bool kid_alg_matched = false;
for (const auto& jwk : jwks.keys()) {
// If kid is specified in JWT, JWK with the same kid is used for
// verification.
// If kid is not specified in JWT, try all JWK.
if (!jwt.kid_.empty() && !jwk->kid_.empty() && jwk->kid_ != jwt.kid_) {
continue;
}
// The same alg must be used.
if (!jwk->alg_.empty() && jwk->alg_ != jwt.alg_) {
continue;
}
kid_alg_matched = true;
if (jwk->kty_ == "EC") {
const EVP_MD* md;
if (jwt.alg_ == "ES384") {
md = EVP_sha384();
} else if (jwt.alg_ == "ES512") {
md = EVP_sha512();
} else {
// default to SHA256
md = EVP_sha256();
}
if (verifySignatureEC(jwk->ec_key_.get(), md, jwt.signature_,
signed_data)) {
// Verification succeeded.
return Status::Ok;
}
} else if (jwk->kty_ == "RSA") {
const EVP_MD* md;
if (jwt.alg_ == "RS384" || jwt.alg_ == "PS384") {
md = EVP_sha384();
} else if (jwt.alg_ == "RS512" || jwt.alg_ == "PS512") {
md = EVP_sha512();
} else {
// default to SHA256
md = EVP_sha256();
}
if (jwt.alg_.compare(0, 2, "RS") == 0) {
if (verifySignatureRSA(jwk->rsa_.get(), md, jwt.signature_,
signed_data)) {
// Verification succeeded.
return Status::Ok;
}
} else if (jwt.alg_.compare(0, 2, "PS") == 0) {
if (verifySignatureRSAPSS(jwk->rsa_.get(), md, jwt.signature_,
signed_data)) {
// Verification succeeded.
return Status::Ok;
}
}
} else if (jwk->kty_ == "oct") {
const EVP_MD* md;
if (jwt.alg_ == "HS384") {
md = EVP_sha384();
} else if (jwt.alg_ == "HS512") {
md = EVP_sha512();
} else {
// default to SHA256
md = EVP_sha256();
}
if (verifySignatureOct(jwk->hmac_key_, md, jwt.signature_, signed_data)) {
// Verification succeeded.
return Status::Ok;
}
} else if (jwk->kty_ == "OKP" && jwk->crv_ == "Ed25519") {
Status status = verifySignatureEd25519(jwk->okp_key_raw_, jwt.signature_,
signed_data);
// For verification failures keep going and try the rest of the keys in
// the JWKS. Otherwise status is either OK or an error with the JWT and we
// can return immediately.
if (status == Status::Ok ||
status == Status::JwtEd25519SignatureWrongLength) {
return status;
}
}
}
// Verification failed.
return kid_alg_matched ? Status::JwtVerificationFail
: Status::JwksKidAlgMismatch;
}
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.