openid / php-openid Goto Github PK
View Code? Open in Web Editor NEWOpenID library for PHP5
License: Apache License 2.0
OpenID library for PHP5
License: Apache License 2.0
I downloaded the latest master today (14.09.2010) and tried to use my i-name in the /exmple/consumer/ script.
So I entered "=this*bjornbjorn" in the openID input box and was sent to https://authn.freexri.com/authentication/ (correct). After entering my password and clicking "Authenticate" there I'm sent back to /examples/consumer/ and the following error message appears:
"OpenID authentication failed: Server denied check_authentication"
When using memcached as store for nonces only the salt is included so that the nonce might not be unique any more if an implementation (e.g. openid4java standard implementation) uses the timestamp followed by a counter.
return $this->connection->add(
'openid_nonce_' . sha1($server_url) . '_' . sha1($salt),
1, // any value here
$this->compress,
$Auth_OpenID_SKEW);
could be modified to:
return $this->connection->add(
'openid_nonce_' . sha1($server_url) . '_' . sha1($timestamp.$salt),
1, // any value here
$this->compress,
$Auth_OpenID_SKEW);
to fix that in Auth/OpenID/MemcachedStore.php
I am getting some PHP Errors as you cannot pass references in function like call_user_function() and call_user_function_array() anymore. It was a warning in PHP5, and it errors out in 5.3.
So '&' should be removed from :
And that's fixed !
I have created issue originally in python-openid library
https://github.com/openid/python-openid/issues#issue/8
Problem is that as we have XRDS document like this:
<?xml version="1.0" encoding="UTF-8"?>
<xrds:XRDS xmlns:xrds="xri://$xrds" xmlns:openid="http://openid.net/xmlns/1.0" xmlns="xri://$xrd*($v*2.0)">
<XRD>
<Service priority="10">
<Type>http://openid.net/server/1.1</Type>
<Type>http://specs.openid.net/extensions/pape/1.0</Type>
<URI>https://test.provider/endpoint/</URI>
</Service>
<Service priority="0">
<Type>http://specs.openid.net/auth/2.0/server</Type>
<Type>http://specs.openid.net/extensions/pape/1.0</Type>
<URI>https://test.provider/endpoint/</URI>
</Service>
</XRD>
</xrds:XRDS>
Repetitive creation of openid requests (run by another reason then unavailable endpoint) results in generating requests with different openid namespace instead of creation of request with the highest priority available as required by specification of YADIS protocol.
In my opinion current implementation partially ignores priorities in XRDS document and uses them only to initial ordering of services.
When using apache mod_rewrite to add query arguments to the request uri return_to parameter mismtach errors could happen, this is because the library gets the arguments from $_SERVER['QUERY_STRING'](which may be modified by mod_rewrite) rather than from $_SERVER['REQUEST_URI'](that has the unmodified original query arguments), this could be fixed by replacing:
$data = Auth_OpenID::params_from_string($_SERVER['QUERY_STRING']);
on line 165 in OpenID.php by:
$url_parts = parse_url($_SERVER['REQUEST_URI']);
$data = Auth_OpenID::params_from_string($url_parts['query']);
I'm writing a provider that uses AX to populate requested fields.
AX.php contains getExtensionArgs functions that are supposed to return the values for an AX response. There is a $request=null param where the function gets information from the request.
In the Unit Tests this method is called passing in the current $request, which is why the unit tests work. However, in an actual application getExtensionArgs is called by toMessage in the Auth_OpenID_Extension class, which never passes a request to getExtensionArgs; this leads to the AX extension never populating any values at all.
It is very very very terrible that after I found this issue already happened when 2007! No one has fixed it until now!
You always get the "Bad signature" response if you don't make this change.
Please! Adds this function for the users who using MySQL (if this project is not dead).
public function blobDecode($blob)
{
return pack('H*', substr($blob, 2));
}
The tarballs that github.com produces seem to be arbitrarily structured. May you post tarballs for php-openid releases at least slightly manually, such as having a subdirectory called packagename-version instead of username-reponame-commitid?
For example, ftp://ohnopub.net/mirror/php-openid-2.2.2.tar.bz2
Thanks.
http://paste.org/pastebin/view/19995
class - interface for store in ZendCache
Please add - very useful for integration in projects.
Probably replace resource in $fetch_url to accessable.
$fetch_url = 'http://www.openidenabled.com/resources/php-fetch-test';
Another nit-pick, tidying up my error log
[Wed Jul 21 21:58:23 2010] [error] [client ::1] PHP Notice: Undefined index: policies in /var/www/.upstream/php-openid/try_auth.php on line 42, referer: http://localhost/
[Wed Jul 21 21:58:23 2010] [error] [client ::1] PHP Stack trace:, referer: http://localhost/
[Wed Jul 21 21:58:23 2010] [error] [client ::1] PHP 1. {main}() /var/www/.upstream/php-openid/try_auth.php:0, referer: http://localhost/
I plan to submit pull requests to resolve this within a few days!
[Wed Jul 21 21:58:23 2010] [error] [client ::1] PHP 2. run() /var/www/.upstream/php-openid/try_auth.php:83, referer: http://localhost/
Hi there, I get this warnings when I use it in a server with PHP version 5.3.
Warning: Call-time pass-by-reference has been deprecated in Auth/OpenID/Server.php on line 1707
Warning: Call-time pass-by-reference has been deprecated in Auth/OpenID/Consumer.php on line 1184
Warning: Call-time pass-by-reference has been deprecated in Auth/Yadis/Manager.php on line 416
The problem as you can see, is that you use a pass by reference in call_user_func (for example in Server.php Line 1707 "return call_user_func($handler, &$request);")
With the new way, you should call "call_user_func" without the pass by reference but you must change the function to accept a pass-by-reference parameter.
for example in Server.php Line 1703 to 1740 it should be
function handleRequest($request)
{
if (method_exists($this, "openid_" . $request->mode)) {
$handler = array($this, "openid_" . $request->mode);
return call_user_func($handler, $request);
}
return null;
}
/**
* The callback for 'check_authentication' messages.
*/
function openid_check_authentication(&$request)
{
return $request->answer($this->signatory);
}
/**
* The callback for 'associate' messages.
*/
function openid_associate(&$request)
{
$assoc_type = $request->assoc_type;
$session_type = $request->session->session_type;
if ($this->negotiator->isAllowed($assoc_type, $session_type)) {
$assoc = $this->signatory->createAssociation(false,
$assoc_type);
return $request->answer($assoc);
} else {
$message = sprintf('Association type %s is not supported with '.
'session type %s', $assoc_type, $session_type);
list($preferred_assoc_type, $preferred_session_type) =
$this->negotiator->getAllowedType();
return $request->answerUnsupported($message,
$preferred_assoc_type,
$preferred_session_type);
}
}
The Auth_OpenID_TrustRoot imlementation has an isSane method that assumes that all 2 letter domain names have at least 2 parts (e.g. .co.uk). This is a wrong assumption; the UK is one of the few 2 letter TLDs that has this feature. Others such as .nl, .de etc only have a single level.
This means that a site such as http://de.nl would be considered 'insane' as a trust root.
http://threatpost.com/en_us/blogs/openid-warns-serious-bug-some-implementations-050611
This article makes it unclear if the OpenID client libs (not just the end-point provider libs) need to be updated to address this security hole. If so, when will this code be updated to address it?
Fatal errors generated in PHP 5.4 (currently in RC):
Fatal error: Call-time pass-by-reference has been removed in \Auth\OpenID\Consumer.php on line 1184
Fatal error: Call-time pass-by-reference has been removed in \Auth\Yadis\Manager.php on line 416
This is something I plan to work on in the near future, however, it's more here in case anyone else wants to create the new SQL layer before I get around to it :)
I just downloaded the code, opened the README which says to first try to run the 'examples/detect.php'.
The page redirects me to detect.php?test_query=a%26b, but loads a blank page. 'display_errors' is ON.
Shouldn't this page work no matter what, as it is supposed to show me what's missing?
I'm running php 5.3 on a localhost.
Hi,
i've installed the lib from rpm package:
Name : php-pear-Auth-OpenID
Arch : noarch
Version : 2.2.2
Release : 1.fc15
Size : 577 k
Repo : installed
From repo : fedora
Summary : PHP OpenID
URL : http://www.janrain.com/openid-enabled
License : ASL 2.0
Description : An implementation of the OpenID single sign-on authentication protocol.
Code: http://pastebin.com/9vfZiCpM
Error message: return_to does not match return URL. Expected http://localhost/gse/signin/complete, got http://localhost/gse/signin/complete?janrain_nonce=2011-07-17T09%3A12%3A23Zzu2wav
OpenRest.eu created a patch which fixed using Google for OpenID on my machine:
http://www.openrest.eu/docs/openid-not-completely-enabled-for-google.php
I've contacted the author to suggest he add the patch to github.
use ' instead of ", this is faster.
use 0/1 instead of FALSE/TRUE.
Change "null" case to Upper. NULL is constant. type
users with underscores are not valid for authenticate with this consumer.
it seems that the method Auth_OpenID_Consumer::begin() don't provide a valid $endpoint if I use an openid with underscores.
is there any workarround for this?
thanks!
Nitzer
Which standard is supported?
openid 2.0 final
openid 2.0 draft 11?
which is the latest supported?
Server.php give me a notice error at line 1471 because $response->code can be undefined. An "isset" test can correct the problem ;)
/* Allow the response to carry a custom error code (ex: for Association errors) */
if(isset($response->code)) {
Warning, do not use this library. This project currently does not have a maintainer.
Obviously, if this changes, the new maintainer should close this issue.
Please see the following thread for justification:
http://lists.openid.net/pipermail/openid-code/2010-December/thread.html#172
Recommend that you consider using the PEAR package which does appear to be maintained at this point: http://pear.php.net/package/OpenID
In Server.php, autosubmit doesn't work if the encode type is set to HTML form. Indeed the $response is currently parsed through toFormMarkup(), whereas it should be toHTML().
This works :
$wr = new $cls(AUTH_OPENID_HTTP_OK, array(), $response->toHTML());
When trying to connect to the google provider (ParanoidHttpFetcher::get with $url = https://www.google.com/accounts/o8/.well-known/host-meta?hd=www.google.com) I got the following error :
CURL error (60): SSL certificate problem, verify that the CA cert is OK. Details:
error:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed
I'm on Windows and it appears that my php/curl install does not have the corresponding certificate built in.
If I manually set the CURLOPT_CAINFO to the path of the OpenID/ca-bundle.crt file, then it solves the problem.
So I added right after $c = curl_init() on line 92 the following line :
curl_setopt($c, CURLOPT_CAINFO, str_replace('', '/', dirname(FILE)).'/../OpenID/ca-bundle.crt');
On complete, I am getting the error:
object(Auth_OpenID_FailureResponse)#44 (6) {
["status"]=>
string(7) "failure"
["endpoint"]=>
NULL
["identity_url"]=>
NULL
["message"]=>
string(34) "Server denied check_authentication"
["contact"]=>
NULL
["reference"]=>
NULL
}
This is occurring in Consumer::_processCheckAuthReponse when getArg(Auth_OpenID_OpenID_NS, 'is_valid','false') is being passed.
The following data is in response:
Object(Auth_OpenID_Message)#57 (4) {
["allowed_openid_namespaces"]=>
array(3) {
[0]=>
string(28) "http://openid.net/signon/1.0"
[1]=>
string(28) "http://openid.net/signon/1.1"
[2]=>
string(32) "http://specs.openid.net/auth/2.0"
}
["args"]=>
object(Auth_OpenID_Mapping)#41 (2) {
["keys"]=>
array(1) {
[0]=>
array(2) {
[0]=>
string(32) "http://specs.openid.net/auth/2.0"
[1]=>
string(8) "is_valid"
}
}
["values"]=>
array(1) {
[0]=>
string(5) "false"
}
}
["namespaces"]=>
object(Auth_OpenID_NamespaceMap)#52 (3) {
["alias_to_namespace"]=>
object(Auth_OpenID_Mapping)#49 (2) {
["keys"]=>
array(1) {
[0]=>
string(14) "Null namespace"
}
["values"]=>
array(1) {
[0]=>
string(32) "http://specs.openid.net/auth/2.0"
}
}
["namespace_to_alias"]=>
object(Auth_OpenID_Mapping)#51 (2) {
["keys"]=>
array(1) {
[0]=>
string(32) "http://specs.openid.net/auth/2.0"
}
["values"]=>
array(1) {
[0]=>
string(14) "Null namespace"
}
}
["implicit_namespaces"]=>
array(0) {
}
}
["_openid_ns_uri"]=>
string(32) "http://specs.openid.net/auth/2.0"
}
Any ideas?
http://lists.openid.net/pipermail/openid-security/2010-July/001156.html
I'm wondering if you guys are aware of this report and if it affects your implementations (specifically your PHP5 one)? I use your lib but I want to know if it's got an issue that needs to be addressed?
There's a header(); command which I somehow doubt is supposed to work on the command line ;)
The purpose of this is to test that query strings aren't corrupted by the server. IMO,
detect_query_corruption($r, $body); should NOT be called on the command line.
dl() was removed in php 5.3, so detect.php just produces blank page because of fatal error in @dl() call.
Simple fix:
if (extension_loaded($dbext) || (function_exists('dl') && dl($dbext . '.' . PHP_SHLIB_SUFFIX))) {
$found[] = $dbext;
}
I know it's a minor nit-pick, but I'm trying to tidy up all the issues in my error log, and this is one of them.
Here's the exact error:
[Wed Jul 21 21:58:21 2010] [error] [client ::1] PHP Notice: Only variable references should be returned by reference in /var/www/.upstream/php-openid/Auth/common.php on line 62, referer: http://localhost/
[Wed Jul 21 21:58:21 2010] [error] [client ::1] PHP Stack trace:, referer: http://localhost/
[Wed Jul 21 21:58:21 2010] [error] [client ::1] PHP 1. {main}() /var/www/.upstream/php-openid/try_auth.php:0, referer: http://localhost/
[Wed Jul 21 21:58:21 2010] [error] [client ::1] PHP 2. run() /var/www/.upstream/php-openid/try_auth.php:83, referer: http://localhost/
[Wed Jul 21 21:58:21 2010] [error] [client ::1] PHP 3. getConsumer() /var/www/.upstream/php-openid/try_auth.php:22, referer: http://localhost/
[Wed Jul 21 21:58:21 2010] [error] [client ::1] PHP 4. getStore() /var/www/.upstream/php-openid/Auth/common.php:70, referer: http://localhost/
If cURL is not installed, HTTPS seems to be silently done using fsockopen, which does not check SSL certificates. IMHO, this should not happen automatically - if a user wants it to work in the insecure way (because curl is unavailable), the user should be required to set the manual override.
Additionally, the Auth_OpenID_VERIFY_HOST constant seems to be read but neither set nor documented anywhere. Thus, it seems that CURLOPT_SSL_VERIFYPEER and CURLOPT_SSL_VERIFYHOST are never set by default. This would only be insecure with cURL versions before 7.10, and I do not know if any PHP5 was ever shipped with such an old version. However, in the current form, the code around Auth_OpenID_VERIFY_HOST makes little sense - either it should be enabled by default (or at least documented) to cover old cURL versions, or it can be removed if cURL >= 7.10 is assumed as it would only set the cURL defaults again. See also Issue #38 which suggests making it possible to override the defaults by setting VERIFY_HOST to false.
Ticket 356 was where once you got a "Bad Signature", it would persist. The code snippet represented here would be applied against the file: Auth/OpenID/Consumer.php
957 957 }
958 958
959 959 if (!$assoc->checkMessageSignature($message)) {
960 // If we get a "bad signature" here, it means that the association
961 // is unrecoverabley corrupted in some way. Any futher attempts
962 // to login with this association is likely to fail. Drop it.
963 $this->store->removeAssociation($server_url, $assoc_handle);
960 964 return new Auth_OpenID_FailureResponse(null,
961 965 "Bad signature");
962 966 }
This was contributed at the time by https://me.yahoo.com/doulepov#5f9d9. It is still an issue with the current version of php-openid.
There are 5 matches in Consumer.php and 9 matches in Server.php. 1 match in AX.php.
They should all be replaced by instanceof operator as of PHP 5.
This currently results in strict errors.
"You can view the HTML library documentation in the doc/ directory."
What doc directory? Could not find any ...
We need this because browser makes encoding by itself, so the value become encoded twice. For example if identifier is an email.
Fixed at my repo:
http://github.com/conf/php-openid/commit/c3ca5c1f7d4c52c649eec93e5a6ffae2030119fd
It'd be better to have the initial Yadis discovery running in a separate method from the begin() method. That way you could still use this new discovery() method to check if the endpoint is valid without triggering the all authentication process. That would also allow to use a different discovery method.
A simple fix could be to rename have all the begin() logic in a discover() method, and have begin() calling this method and then calling beginWithoutDiscovery() if the endpoint is found or returning NULL if not.
I can't log use open ID because of the method useNonce in FileStore.php
$result = @fopen($filename, 'x');
if ($result === false) {
debug_file($filename);
return false;
}
my debug says
D:\data\openid\nonces\4d895...FSRy0 exists
D:\data\openid\nonces\4d895...FSRy0 is readable
D:\data\openid\nonces\4d895...FSRy0 is writable
I'm not sure why this happens
A) I successfully redirect to the openid provider
$authRequest = $consumer->begin($id);
$authRequest = $consumer->begin($id);
if (is_null($authRequest)) {
return new Zend_Auth_Result( Zend_Auth_Result::FAILURE, $id, array("Authentication failed", 'Unknown error'));
}
if (Auth_OpenID::isFailure($authRequest)) {
return new Zend_Auth_Result(Zend_Auth_Result::FAILURE,$id,array("Authentication failed", "Could not redirect to server: " . $authRequest->message)); }
$redirectUrl = $authRequest->redirectUrl($this->_root, $this->_returnTo);
if (Auth_OpenID::isFailure($redirectUrl)) {
return new Zend_Auth_Result( Zend_Auth_Result::FAILURE,
$id, array("Authentication failed", $redirectUrl->message));
}
Zend_OpenId::redirect($redirectUrl);
B) sucesfully log in
C) come back and
$consumer = new Auth_OpenID_Consumer($this->_storage);
$response = $consumer->complete(Zend_OpenId::selfUrl());
switch($response->status) {
case Auth_OpenID_CANCEL:
case Auth_OpenID_FAILURE:
return new Zend_Auth_Result(
Zend_Auth_Result::FAILURE,
null,
array("Authentication failed. " . @$response->message));
break;
case Auth_OpenID_SUCCESS:
return $this->_constructSuccessfulResult($response);
break;
}
Get this error "Nonce already used or out of range"
What could be the reason for this?
It seems impossible to authorize on <user_name>.moikrug.ru site using this library. But I can authorize by this OpenID at livejournal.com, for example.
examples/consumer/index.php doesn't work with livejournal.com
I try to use my openid "bolk.livejournal.com" and get blank page with URL http://somemyexamplehost.ru/examples/consumer/try_auth.php?action=verify&openid_identifier=http://bolk.livejournal.com
This warning is generated when the user has cookies disabled.
Auth/Yadis/Manager.php:40
38 function get($name, $default=null)
39 {
40 if (array_key_exists($name, $_SESSION)) {
41 return $_SESSION[$name];
42 } else {
43 return $default;
44 }
45 }
Backtrace
1 array_key_exists(yadis_services__openid_consumer, ) called at [/usr/share/php/Auth/Yadis/Manager.php:40]
2 Auth_Yadis_PHPSession->get(yadis_services__openid_consumer) called at [/usr/share/php/Auth/Yadis/Manager.php:474]
3 Auth_Yadis_Discovery->getManager(1) called at [/usr/share/php/Auth/Yadis/Manager.php:443]
4 Auth_Yadis_Discovery->cleanup(1) called at [/usr/share/php/Auth/OpenID/Consumer.php:440]
5 Auth_OpenID_Consumer->complete() ...
...
I think that I use the "Auth_OpenID_DumbStore" class when executing it with stateless(dumb) mode.
When Consumer.php line 618 is seen, it seems to do the association even when Auth_OpenID_DumbStore is used.
Is this correct processing?
Consumer.php line 618
function Auth_OpenID_GenericConsumer($store)
{
$this->store = $store;
$this->negotiator = Auth_OpenID_getDefaultNegotiator();
$this->_use_assocs = (is_null($this->store) ? false : true); <-- it's true even if "Auth_OpenID_DumbStore"
$this->fetcher = Auth_Yadis_Yadis::getHTTPFetcher();
$this->session_types = Auth_OpenID_getAvailableSessionTypes();
}
The log method should be modified to take a severity parameter (E_USER_NOTICE, E_USER_WARNING, E_USER_ERROR) and use trigger_error() instead of error_log().
http://php.net/manual/en/function.error-log.php
http://php.net/manual/en/function.trigger-error.php
http://php.net/manual/en/errorfunc.constants.php
With trigger_error(), errors will go to the log when they match the error_reporting level. They can also be caught at runtime using set_error_handler() and inspected (backtrace, session state, etc.), displayed, emailed, or otherwise managed.
http://php.net/manual/en/errorfunc.configuration.php#ini.error-reporting
http://php.net/manual/en/function.set-error-handler.php
ParanoidHTTPFetcher contains a couple of stanzas of:
if (defined('Auth_OpenID_VERIFY_HOST')) {
curl_setopt($c, CURLOPT_SSL_VERIFYPEER, true);
curl_setopt($c, CURLOPT_SSL_VERIFYHOST, 2);
}
The trouble is, CURLOPT_SSL_VERIFYPEER = true by default for recent releases of curl [1], so this setting isn't actually doing anything. And getting VERIFYPEER to actually work properly has been extremely problematic for me [2], although that could be because I'm running on Windows. Anyway, it'd be very helpful if Auth_OpenID_VERIFY_HOST was a boolean, and when false it explictly set CURLOPT_SSL_VERIFYPEER = false.
[1] http://us2.php.net/manual/en/function.curl-setopt.php
[2] http://unitstep.net/blog/2009/05/05/using-curl-in-php-to-access-https-ssltls-protected-sites/
In Server.php, this function calls the Auth_OpenID_verifyReturnTo() function from TrustRoot.php, which takes 3 arguments, but only 2 are given in call_user_func_array.
This works :
function returnToVerified()
{
$fetcher = Auth_Yadis_Yadis::getHTTPFetcher();
return call_user_func_array($this->verifyReturnTo, array($this->trust_root, $this->return_to, $fetcher));
}
It is clearly stated in the class comments that PEAR can be replaced with a class extending DatabaseConnection. This is for good, I don't have PEAR installed on my system and don't want to install it.
However, SQLStore still uses PEAR :
function isError($value)
{
return PEAR::isError($value);
}
I have set it to return FALSE at the minute as I am not too sure what to do (perhaps check for PEAR existence and if not check for parent isError() existence). Anyway, you can't get it to work if you don't have PEAR installed and even if so this function doesn't make sense.
Some class comments should be corrected as well, as sometimes it only refers to PEAR.
Hello all,
I have the latest version of Janrain's library (v2.2.2) for OpenID. I'm under php 5.2 and 5.3 (2 servers)
The following endpoints will work :
But "yahoo.com" will fail. The discover.php example given with the library also fails.
Does anyone have the same issue ?
My implementation can be found here : http://fr.quizity.com
Thanks a lot
A Debian user reported that the regular expression in Auth/OpenID/Parse.php is too greedy to cope with big pages. A simple fix is to make .? to .
See http://bugs.debian.org/535479 for more info.
Per the install instructions, I added /path/to/openid-php/Auth to my PHP include path. This threw an error for a missing file, "Auth/Yadis/..."
I changed the include_path to /path/to/openid-php, and all went well. So maybe the instructions should be to add the parent directory to the path, not the Auth directory...?
It seems that setup.php generates the wrong configuration in SQLite.
It generates something like:
$s = new Auth_OpenID_SQLiteStore ('pathtosqlitedbfile');
While it should generate something similair to MySQL with:
require_once ('DB.php');
$db =& DB::connect($dsn);
Because Auth_OpenID_SQLiteStore expects a PEAR-DB-connection-object.
Hope this helps.
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.