oauth-io / sdk-node Goto Github PK
View Code? Open in Web Editor NEWOAuth that just works ! This is the Node.js SDK for OAuth.io
Home Page: https://oauth.io
OAuth that just works ! This is the Node.js SDK for OAuth.io
Home Page: https://oauth.io
I'm trying to test my code I've written using supertest and nock but I'm getting a state not matching error.
My package.json dep section:
{
"oauthio": "^0.3.1",
"express": "~4.0.0",
"express-session": "~1.0.2"
}
My package.json dev section:
{
"nock": "^0.48.2",
"querystring": "^0.2.0",
"should": "~3.3.1",
"supertest": "~0.11.0"
}
I've set up some nock listeners on two endpoints.
My server-side code is:
exports.index = function(req, res) {
// .....
OAuth.auth('facebook', req.session, {
code: code
}).then(function (reqObj) {
return reqObj.get('/me');
}).then(function (info) {
console.log(info);
// ......
});
};
My test:
describe('My Test', function() {
beforeEach(function (done) {
// .....
nock('https://oauth.io')
.post('/auth/access_token', {
code: 'someCode',
key: config.oauth.appKey,
secret: config.oauth.appSecret
})
.reply(200, {
access_token: 'result_access_token',
expires_in: 'someday',
request: {},
state: 'unique_id',
provider: 'facebook'
});
nock('https://oauth.io')
.matchHeader('oauthio', qs.stringify({
k: config.oauth.appKey,
access_token: 'result_access_token'
}))
.get('/request/facebook/me')
.reply(200, {
email: '[email protected]',
first_name: 'Foo',
last_name: 'Bar'
});
done();
});
afterEach(function (done) {
// .....
});
});
it('should respond with a successful login', function(done) {
request(app)
.post('/api/user/login/facebook')
.set('cookie', cookie)
.send({code: 'someCode'})
.expect(200)
.expect('Content-Type', /json/)
.end(function(err, res) {
if (err) return done(err);
res.body.should.be.instanceof(Object);
done();
});
});
However, my response on the test is:
[Error: State is not matching]
Any thoughts on why I'm getting this error?
Receiving 'Error: req.session must be an object (did you used a session middleware?)' error when attempting to follow examples in documentation (using Node.js). My code is below, all dependencies installed I think.
Apologies if this is the wrong place to post.
// Example with Linkedin
app.get('/signin', OAuth.auth('linkedin', 'MY_APP_URL/oauth/redirect'));
app.get('/oauth/redirect', OAuth.redirect(function(result, req, res) {
if (result instanceof Error) {
res.send(500, "error: " + result.message);
}
result.me().done(function(me) {
console.log(me);
res.send(200, JSON.stringify(me));
});
}));
I am trying to authenticate user on server side (node.js based REST API) as it described here: https://github.com/oauth-io/sdk-node#auth-method, section 'Authenticating the user from the frontend JS SDK'.
Client side (Phonegap oauth.io SDK) works OK and returns code after successful authentication.
Server side code:
// Called before auth from client
oauthioStateToken: function(req, res, next) {
res.json({
// Our API does not use session, I pass empty object here for now.
token: oauthio.generateStateToken({})
});
},
// Called after auth from client
google: function(req, res, next) {
if (!req.body || !req.body.code) {
return next(new BadRequestError('No Google access code found'));
}
// Again, empty object instead of session for now.
// This call fails with Error: State is missing from response
oauthio.auth('google_plus', {} , { code: req.body.code })
.then(function(reqObject) {
return reqObject.get("/me");
})
.then(function(info) {
res.json(info);
})
.fail(next);
},
Trying to track down a problem I found this code (oauth.is node.js SDK, file lib/authentication.js):
authenticate: function(code, session) {
var defer;
defer = Q.defer();
request.post({
url: cache.oauthd_url + cache.oauthd_base + '/access_token', // https://oauth.io/auth/access_token
form: {
code: code, // code, received from client
key: cache.public_key, // public key from oauth.io. I checked, it matches with client one
secret: cache.secret_key
}
}, function(e, r, body) {
// Body is {"status":"fail","data":{"code":"invalid or expired"}}
// Seems like `response.status=="fail"` is unhandled and it falls to
// "Error State is missing from response" (see below).
var response, _ref;
if (e) {
defer.reject(e);
return;
}
try {
response = JSON.parse(body);
} catch (_error) {
e = _error;
defer.reject(new Error('OAuth.io response could not be parsed'));
return;
}
if ((response.status != null) && response.status === 'error' && (response.message != null)) {
defer.reject(new Error('OAuth.io / oauthd responded with : ' + response.message));
}
if (response.state == null) {
defer.reject(new Error('State is missing from response'));
return;
}
if (((session != null ? session.csrf_tokens : void 0) == null) || (_ref = response.state, __indexOf.call(session.csrf_tokens, _ref) < 0)) {
defer.reject(new Error('State is not matching'));
}
if (response.expires_in) {
response.expires = new Date().getTime() + response.expires_in * 1000;
}
response = a.construct_request_object(response);
if ((session != null)) {
session.oauth = session.oauth || {};
session.oauth[response.provider] = response;
}
return defer.resolve(response);
});
return defer.promise;
}
I double checked all settings. Public/Secret keys matching. Added localhost and * to domains whitelist (I am testing from localhost). Maybe I messed something important?
Thank you!
I'm trying to authenticate my app with oauth-phonegap, and that seems to work, but I need to send the code to my server. That's where I'm having problems. Here's the error:
root@example:~/example-api# node index
Express server listening on port 3000 in development mode
GET /oauth/token 200 22.302 ms - 26
req.session: { cookie:
{ path: '/',
_expires: null,
originalMaxAge: null,
httpOnly: true },
passport: {} }
code: IM676Ffknwwzz6rEc6BOPNy5bcV
TypeError: Cannot read property 'google' of undefined
at Object.a.auth (/root/example-api/node_modules/oauthio/js/lib/authentication.js:86:26)
at Object.oauth.auth (/root/example-api/node_modules/oauthio/js/main.js:84:31)
at module.exports (/root/example-api/app/server.js:83:11)
at Layer.handle [as handle_request] (/root/example-api/node_modules/express/lib/router/layer.js:82:5)
at next (/root/example-api/node_modules/express/lib/router/route.js:110:13)
at Route.dispatch (/root/example-api/node_modules/express/lib/router/route.js:91:3)
at Layer.handle [as handle_request] (/root/example-api/node_modules/express/lib/router/layer.js:82:5)
at /root/example-api/node_modules/express/lib/router/index.js:267:22
at Function.proto.process_params (/root/example-api/node_modules/express/lib/router/index.js:321:12)
at next (/root/example-api/node_modules/express/lib/router/index.js:261:10)
at serveStatic (/root/example-api/node_modules/express/node_modules/serve-static/index.js:59:14)
at Layer.handle [as handle_request] (/root/example-api/node_modules/express/lib/router/layer.js:82:5)
at trim_prefix (/root/example-api/node_modules/express/lib/router/index.js:302:13)
at /root/example-api/node_modules/express/lib/router/index.js:270:7
at Function.proto.process_params (/root/example-api/node_modules/express/lib/router/index.js:321:12)
at next (/root/example-api/node_modules/express/lib/router/index.js:261:10)
at /root/example-api/node_modules/express-flash/lib/express-flash.js:31:7
at /root/example-api/node_modules/express-flash/node_modules/connect-flash/lib/flash.js:21:5
at /root/example-api/node_modules/express-flash/lib/express-flash.js:22:5
at Layer.handle [as handle_request] (/root/example-api/node_modules/express/lib/router/layer.js:82:5)
at trim_prefix (/root/example-api/node_modules/express/lib/router/index.js:302:13)
at /root/example-api/node_modules/express/lib/router/index.js:270:7
at Function.proto.process_params (/root/example-api/node_modules/express/lib/router/index.js:321:12)
at next (/root/example-api/node_modules/express/lib/router/index.js:261:10)
at SessionStrategy.strategy.pass (/root/example-api/node_modules/passport/lib/middleware/authenticate.js:318:9)
at SessionStrategy.authenticate (/root/example-api/node_modules/passport/lib/strategies/session.js:67:10)
at attempt (/root/example-api/node_modules/passport/lib/middleware/authenticate.js:341:16)
at authenticate (/root/example-api/node_modules/passport/lib/middleware/authenticate.js:342:7)
at Layer.handle [as handle_request] (/root/example-api/node_modules/express/lib/router/layer.js:82:5)
at trim_prefix (/root/example-api/node_modules/express/lib/router/index.js:302:13)
at /root/example-api/node_modules/express/lib/router/index.js:270:7
at Function.proto.process_params (/root/example-api/node_modules/express/lib/router/index.js:321:12)
at next (/root/example-api/node_modules/express/lib/router/index.js:261:10)
at initialize (/root/example-api/node_modules/passport/lib/middleware/initialize.js:62:5)
at Layer.handle [as handle_request] (/root/example-api/node_modules/express/lib/router/layer.js:82:5)
at trim_prefix (/root/example-api/node_modules/express/lib/router/index.js:302:13)
at /root/example-api/node_modules/express/lib/router/index.js:270:7
at Function.proto.process_params (/root/example-api/node_modules/express/lib/router/index.js:321:12)
at next (/root/example-api/node_modules/express/lib/router/index.js:261:10)
at session (/root/example-api/node_modules/express-session/index.js:386:7)
at Layer.handle [as handle_request] (/root/example-api/node_modules/express/lib/router/layer.js:82:5)
at trim_prefix (/root/example-api/node_modules/express/lib/router/index.js:302:13)
at /root/example-api/node_modules/express/lib/router/index.js:270:7
at Function.proto.process_params (/root/example-api/node_modules/express/lib/router/index.js:321:12)
at next (/root/example-api/node_modules/express/lib/router/index.js:261:10)
at cookieParser (/root/example-api/node_modules/cookie-parser/index.js:34:14)
at Layer.handle [as handle_request] (/root/example-api/node_modules/express/lib/router/layer.js:82:5)
at trim_prefix (/root/example-api/node_modules/express/lib/router/index.js:302:13)
at /root/example-api/node_modules/express/lib/router/index.js:270:7
at Function.proto.process_params (/root/example-api/node_modules/express/lib/router/index.js:321:12)
at next (/root/example-api/node_modules/express/lib/router/index.js:261:10)
at methodOverride (/root/example-api/node_modules/method-override/index.js:77:5)
at Layer.handle [as handle_request] (/root/example-api/node_modules/express/lib/router/layer.js:82:5)
at trim_prefix (/root/example-api/node_modules/express/lib/router/index.js:302:13)
at /root/example-api/node_modules/express/lib/router/index.js:270:7
at Function.proto.process_params (/root/example-api/node_modules/express/lib/router/index.js:321:12)
at next (/root/example-api/node_modules/express/lib/router/index.js:261:10)
at /root/example-api/node_modules/express-validator/lib/express_validator.js:233:12
at Layer.handle [as handle_request] (/root/example-api/node_modules/express/lib/router/layer.js:82:5)
at trim_prefix (/root/example-api/node_modules/express/lib/router/index.js:302:13)
at /root/example-api/node_modules/express/lib/router/index.js:270:7
at Function.proto.process_params (/root/example-api/node_modules/express/lib/router/index.js:321:12)
at next (/root/example-api/node_modules/express/lib/router/index.js:261:10)
at /root/example-api/node_modules/multer/index.js:218:19
at Layer.handle [as handle_request] (/root/example-api/node_modules/express/lib/router/layer.js:82:5)
at trim_prefix (/root/example-api/node_modules/express/lib/router/index.js:302:13)
at /root/example-api/node_modules/express/lib/router/index.js:270:7
at Function.proto.process_params (/root/example-api/node_modules/express/lib/router/index.js:321:12)
at next (/root/example-api/node_modules/express/lib/router/index.js:261:10)
at urlencodedParser (/root/example-api/node_modules/body-parser/lib/types/urlencoded.js:77:44)
at Layer.handle [as handle_request] (/root/example-api/node_modules/express/lib/router/layer.js:82:5)
at trim_prefix (/root/example-api/node_modules/express/lib/router/index.js:302:13)
at /root/example-api/node_modules/express/lib/router/index.js:270:7
at Function.proto.process_params (/root/example-api/node_modules/express/lib/router/index.js:321:12)
at next (/root/example-api/node_modules/express/lib/router/index.js:261:10)
at /root/example-api/node_modules/body-parser/lib/read.js:111:5
at IncomingMessage.onEnd (/root/example-api/node_modules/body-parser/node_modules/raw-body/index.js:136:7)
at IncomingMessage.g (events.js:180:16)
at IncomingMessage.EventEmitter.emit (events.js:92:17)
at _stream_readable.js:920:16
at process._tickCallback (node.js:415:13)
POST /oauth/signin 500 40.794 ms - -
Here's the server code:
var MongoStore, app, bodyParser, compress, connectAssets, cookieParser, errorHandler, express, expressValidator, flash, logger, methodOverride, mongoose, multer, oauth, passport, passportConf, path, secrets, session;
express = require('express');
cookieParser = require('cookie-parser');
compress = require('compression');
session = require('express-session');
bodyParser = require('body-parser');
logger = require('morgan');
errorHandler = require('errorhandler');
methodOverride = require('method-override');
multer = require('multer');
MongoStore = require('connect-mongo')(session);
flash = require('express-flash');
path = require('path');
mongoose = require('mongoose');
passport = require('passport');
expressValidator = require('express-validator');
connectAssets = require('connect-assets');
oauth = require('oauthio');
secrets = require('./config/secrets');
passportConf = require('./config/passport');
app = express();
mongoose.connect(secrets.db);
mongoose.connection.on('error', function() {
console.error('MongoDB Connection Error. Please make sure that MongoDB is running.');
});
app.set('port', process.env.PORT || 3000);
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');
app.use(compress());
app.use(connectAssets({
paths: [path.join(__dirname, 'public/css'),
path.join(__dirname, 'public/js')]
}));
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({
extended: false
}));
app.use(multer({
dest: path.join(__dirname, 'uploads')
}));
app.use(expressValidator());
app.use(methodOverride());
app.use(cookieParser('secret'));
app.use(session({
resave: true,
saveUninitialized: true,
secret: secrets.sessionSecret,
store: new MongoStore({
url: secrets.db,
autoReconnect: true
})
}));
app.use(passport.initialize());
app.use(passport.session());
app.use(flash());
app.use(express["static"](path.join(__dirname, 'public'), {
maxAge: 31557600000
}));
app.get('/oauth/token', function(req, res) {
var token = oauth.generateStateToken(req.session);
return res.send(token);
});
app.post("/oauth/signin", function(req, res) {
var code = req.body.data;
console.log("\n req.session: ", req.session + "\n");
oauth.auth("google", req.session, console.log("code: ", req.body.data + "\n"), {
code: code // right after this is where we have problems
}).then(function(request_object) {
debugger;
return res.send(200, "The user is authenticated");
}).fail(function(e) {
debugger;
console.log(e);
return res.send(400, "Code is incorrect");
});
return res.redirect(req.session.returnTo || '/');
});
app.use(errorHandler());
app.listen(app.get("port"), function() {
console.log("Express server listening on port %d in %s mode", app.get("port"), app.get("env"));
});
module.exports = app;
And in case anyone cares, here's the client side controller:
var app = angular.module('app');
app.controller('LoginCtrl', function($scope, $state, UserService, $http, $cookies) {
// initialize oauth with our app id: success
OAuth.initialize('LONG_STRING');
var token = null;
// retrieve token from server: success
$http.get('http://api.example.com/oauth/token').success(function(generatedToken, status) {
return token = generatedToken;
}).error(function(errorMessage, status) {
return console.log("error at LoginCtrl initiation\n " + status + ": " + errorMessage);
});
// called when user clicks the google login button
$scope.googleLogin = function() {
return OAuth.popup('google', {
cache: false,
state: token
}).done(function(result) {
// we make it all the way here, but this is as far as our journey takes us
return $http.post("http://api.example.com/oauth/signin", {
data: result.code
}).success(function(data, status) {
debugger;
}).error(function(data, status) {
debugger;
});
}).fail(function(error) {
return console.log('error: ', error);
});
};
I'm not really sure what the error means, and I was hoping someone could offer some guidance.
Github API rejects simple API call GET /users after successful login
https://developer.github.com/v3/users/#get-a-single-user
API responce
{ error: null,
body: '{"serviceErrorCode":401,"message":"Empty oauth2 access token","status":401}',
status: 401,
message: 'Unauthorized' }
app.get('/api', function (req, res){
oauth.auth('linkedin', req.session, {
credentials: req.session.credentials
}).then(function (request_object) {
return request_object.get('/v2/me', {
});
})
.then(function (r) {
res.status(200).send('<pre>' + JSON.stringify(r) + '</pre>');
})
.fail(function (e) {
res.status(400).send('An error occured while posting the message');
});
});
package.json
"dependencies": {
"cors": "^2.8.4",
"express": "^4.16.3",
"express-session": "^1.15.6",
"npm": "^5.8.0",
"oauthio": "^0.3.5",
"session-file-store": "^1.2.0",
"to": "^0.2.9",
"update": "^0.7.4"
},
"devDependencies": {
"nodemon": "^1.17.2"
}
request_object
{ oauth_token: 'xxxxxxxxxxxxxxxxx',
oauth_token_secret: 'xxxxxxxxxxxxxx',
request: { url: 'https://api.linkedin.com' },
state: 'xxxxxxxxxxxxx',
provider: 'linkedin',
refreshed: false,
get: [Function],
post: [Function],
patch: [Function],
put: [Function],
del: [Function],
me: [Function],
getCredentials: [Function],
wasRefreshed: [Function] }
I've spent hours trying to get this to work with Express 4. I get the following error...
TypeError: Cannot read property 'csrf_tokens' of undefined
I've followed the advice in this post - #2 but it still doesn't work.
Someone please help I'm going mad.
Thanks
I have the basic server side authentication working as expected on my local host. When I deploy to Heroku and test there however I am getting a "Bad Parameters" error. I've narrowed this down to the callback url. If I set it to my local host it works fine, if I set it to the remote heroku app it doesn't like it
I've tried both http and https but that doesn't change a thing. The call back url looks like this...
https://nameofmyapp.herokuapp.com/callback
Is there any reason why this shouldn't work? Are subdomains a problem?
Hello, oauth.io.
I've set up two routes using OAuth.auth
and OAuth.redirect
in my Node.js app with Express. I forgot to add my domain to the whitelist and tried to visit the auth route. It called back to my redirect route with the error, but did so like this:
/redirect#oauthio=<url-encoded-json>
...rather than this:
/redirect?oauthio=<url-encoded-json>
That caused it to relay this error:
Error: Could not find oauthio in query string
Changing the #
to a ?
resulted in the expected error:
Error: Origin "<redirect-url>" does not match any registered domain/url on oauth.io
I assume this is not intended?
Thanks!
Hi, I am getting an error where the appsecret_proof is not being send when I issue a request_object.me() What is the correct setup to get this to be passed with the OAuth.io libraries ? I have managed to use the fbgraph Node.js package and can set the token and secret and this works with accessing the FB graph ? I am trying to write a generic Node.js auth package for Social so would like to use me() api for multiple social network integration.
Any help on this would be useful.
Thanks
Matt
I followed the documentation and was able to successfully get the state token, get the auth code from facebook and send the authcode to my server but when I try to get the facebook access token from my backend I get the above error.
I also noticed that Auth.setOAuthdURL isn't working in sdk-node. How can I tell the framework that I want it to connect to oauthd and not oauth.io
Please help
Seeing intermittent errors calling OAuth.auth()
, where it falls into the fail()
block. The err
is simply:
[Error: State is not matching]
What does that mean? We are using redis to store sessions by the way.
Here is the full block of code we are using:
////
// OAuth signin route
////
app.post('/oauth/signin', [
oauthLogger,
jsonParser,
function(req, res) {
if(!req.body) {
res.status(400).send({
error: {
status_code: 400,
status: 'Bad Request',
message: 'Missing required body'
}
}).end();
return;
}
if(!req.body.provider || !req.body.code) {
res.status(400).send({
error: {
status_code: 400,
status: 'Bad Request',
message: 'Missing required parameter'
}
}).end();
return;
}
OAuth.auth(req.body.provider, req.session, {
code: req.body.code
})
.then(function(requestObject) {
return requestObject.me();
})
.then(function(me) {
if(!me.raw.id) {
res.status(500).send({
error: {
status_code: 500,
status: 'Internal Server Error',
message: 'Missing required id'
}
}).end();
return;
}
req.session.oauth.provider = req.body.provider;
req.session.oauth.id = me.raw.id;
res.status(200).send({
email: me.email,
avatar: me.avatar
}).end();
})
.fail(function(err) {
res.status(503).send({
error: {
status_code: 503,
status: 'Service Unavailable',
message: err.toString()
}
}).end();
});
}
]);
@william26 Thanks for fixing the previous issues! Great help but now there is a new error message: 'POST is not allowed'. here's the code i'm using on my backend after i've obtained the state_token and the auth_code:
OAuth.auth(req.params.api, req.session, {
code: req.params.authCode
})
.then(function (request_object) {
return request_object.get('/me');
})
.then(function (info) {
var user = {
email: info.email
};
res.send(200, 'Successfully logged in');
})
.fail(function (e) {
res.send(400, 'An error occured: ' + e);
});
Linkedin api is not url form encoded. Therefore one should not set form properties on request object and set body property. If json is sent one should set header by setting json property on request object.
Furthermore status code returned by linkedin is 201 not 200.
This is what I've changed in your request.js file in order to make post working with json format:
line 52 in request.js
options = { method: method, url: url, headers: headers, form: !options.json ? options : undefined, json: options.json, qs: get_options };
...
line 63 in request.js
if ((body != null) && (r.statusCode === 200 || r.statusCode === 201)) {
I'm going to fork the project.
Is this sdk compatible with other NodeJS frameworks? It seems like it might be middleware for expressjs?
Ray wants segment.io
It doesn't seem that cache.csrf_tokens is ever used. Am I misunderstanding something in the code? Was there a purpose meant for it?
I want to authorise multiple providers on my app and I'm just wondering if there is a way of doing it without having to create a separate endpoint for each one.
Something like this is what I'm after but the authorisation function doesn't seem to work when wrapped in a container function.
app.get('/signin', function(req, res){
OAuth.auth(req.param.provider, 'http://localhost:8080/oauth/redirect')
});
Github API rejects simple API call GET /users after successful login
https://developer.github.com/v3/users/#get-a-single-user
API responce
{ error: null,
body: 'Request forbidden by administrative rules. Please make sure your request has a User-Agent header (http://developer.github.com/v3/#user-agent-required). Check https://developer.github.com for other possible causes.\n',
status: 403,
message: 'Forbidden' }
app.get('/api', function (req, res){
oauth.auth('github', req.session, {
credentials: req.session.credentials
}).then(function (request_object) {
return request_object.get('/user', {
});
})
.then(function (r) {
res.status(200).send('<pre>' + JSON.stringify(r) + '</pre>');
})
.fail(function (e) {
res.status(400).send('An error occured while posting the message');
});
});
package.json
"dependencies": {
"cors": "^2.8.4",
"express": "^4.16.3",
"express-session": "^1.15.6",
"npm": "^5.8.0",
"oauthio": "^0.3.5",
"session-file-store": "^1.2.0",
"to": "^0.2.9",
"update": "^0.7.4"
},
"devDependencies": {
"nodemon": "^1.17.2"
}
request_object
{ access_token: 'xxxxxxxxxxxxxxxxx',
token_type: 'bearer',
request:
{ url: 'https://api.github.com',
cors: true,
headers: { Authorization: 'Bearer {{token}}' } },
state: 'xxxxxxxxxxxxxxxxx',
provider: 'github',
refreshed: false,
get: [Function],
post: [Function],
patch: [Function],
put: [Function],
del: [Function],
me: [Function],
getCredentials: [Function],
wasRefreshed: [Function] }
I faced a TypeError: Cannot read property 'csrf_tokens' of undefined
error when I followed your tutorial. From my understanding it's coming from the latest version of expresse that does not include those middleware by default.
My suggested workaround
npm install csurf cookie-parser express-session
Add:
var csrf = require('csurf');
var cookieParser = require('cookie-parser');
var session = require('express-session')
app.use(cookieParser());
app.use(session({secret: 'keyboard cat'}))
app.use(csrf());
app.use(function(req, res, next) {
res.cookie('XSRF-TOKEN', req.csrfToken());
next();
});
and it should be fine ๐
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.