Coder Social home page Coder Social logo

feathersjs-ecosystem / authentication-jwt Goto Github PK

View Code? Open in Web Editor NEW
30.0 5.0 10.0 217 KB

[MOVED] JWT authentication strategy for feathers-authentication using Passport

Home Page: https://github.com/feathersjs/feathers

License: MIT License

JavaScript 100.00%

authentication-jwt's Introduction

@feathersjs/authentication-jwt

Important: The code for this module has been moved into the main Feathers repository at feathersjs/feathers (package direct link). Please open issues and pull requests there. No changes in your existing Feathers applications are necessary.

JWT authentication strategy for feathers-authentication using Passport

Installation

npm install @feathersjs/authentication-jwt --save

Quick example

const feathers = require('@feathersjs/feathers');
const authentication = require('feathers-authentication');
const jwt = require('@feathersjs/authentication-jwt');
const app = feathers();

// Setup authentication
app.configure(authentication(settings));
app.configure(jwt());

// Setup a hook to only allow valid JWTs to authenticate
// and get new JWT access tokens
app.service('authentication').hooks({
  before: {
    create: [
      authentication.hooks.authenticate(['jwt'])
    ]
  }
});

Documentation

Please refer to the @feathersjs/authentication-jwt documentation for more details.

License

Copyright (c) 2018

Licensed under the MIT license.

authentication-jwt's People

Contributors

bernardobelchior avatar corymsmith avatar daffl avatar ekryski avatar greenkeeper[bot] avatar marshallswain avatar timelesshaze avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

authentication-jwt's Issues

Strategy succeeds even if user is not found

If a user signs in and the server later removes the user from the database while the JWT is still valid, the strategy will attempt to find the user. If it does not find it, the strategy still succeeds.
There is a missing existance check at verifier.js:30.

If there is no user found, the strategy should fail. Likewise, if the user lookup/population fails for another reason, the strategy should also fail, as indicated at verifier.js:36

Edit: The only thing that needs to change is verifier.js:36 which should return done(error) rather than done(null, {}, payload).

feathers-authentication-jwt:verify Looking up user by id 5606d82e-69bc-49fc-8d14-1456a8776ec9 +2ms
feathers-errors NotFound(404): No record found for id '5606d82e-69bc-49fc-8d14-1456a8776ec9' +198ms
feathers-errors {} +1ms
info: error: users - Method: get: No record found for id '5606d82e-69bc-49fc-8d14-1456a8776ec9'
error:  Error
    at Object.<anonymous> (E:\Development\FeathersJS\meta-drm\node_modules\feathers-errors\lib\index.js:70:27)
    at Module._compile (module.js:571:32)
    at Module._extensions..js (module.js:580:10)
    at Object.require.extensions.(anonymous function) [as .js] (E:\Development\FeathersJS\meta-drm\node_modules\babel-register\lib\node.js:152:7)
    at Module.load (module.js:488:32)
    at tryModuleLoad (module.js:447:12)
    at Function.Module._load (module.js:439:3)
    at Module.require (module.js:498:17)
    at require (internal/module.js:20:19)
    at Object.<anonymous> (E:\Development\FeathersJS\meta-drm\node_modules\feathers-rest\lib\wrappers.js:13:23)
feathers-authentication-jwt:verify Error populating user with id 5606d82e-69bc-49fc-8d14-1456a8776ec9 { Error
    at Object.<anonymous> (E:\Development\FeathersJS\meta-drm\node_modules\feathers-errors\lib\index.js:70:27)
    at Module._compile (module.js:571:32)
    at Module._extensions..js (module.js:580:10)
    at Object.require.extensions.(anonymous function) [as .js] (E:\Development\FeathersJS\meta-drm\node_modules\babel-register\lib\node.js:152:7)
    at Module.load (module.js:488:32)
    at tryModuleLoad (module.js:447:12)
    at Function.Module._load (module.js:439:3)
    at Module.require (module.js:498:17)
    at require (internal/module.js:20:19)
    at Object.<anonymous> (E:\Development\FeathersJS\meta-drm\node_modules\feathers-rest\lib\wrappers.js:13:23)
  type: 'FeathersError',
  name: 'NotFound',
  message: 'No record found for id \'5606d82e-69bc-49fc-8d14-1456a8776ec9\'',
  code: 404,
  className: 'not-found',
  data: undefined,
  errors: {} } +6ms
feathers-authentication:passport:authenticate 'jwt' authentication strategy succeeded {} { userId: '5606d82e-69bc-49fc-8d14-1456a8776ec9',
  iat: 1499935648,
  exp: 1500022048,
  aud: 'https://yourdomain.com',
  iss: 'feathers',
  sub: 'anonymous' } +5ms

This issue leads to my users still being signed in even after their user is deleted, the only difference is that their user object is undefined.

Include data into JWT response

How can I insert some more information into JWT response? For example, include the name and email of logged user, just to show it in a fixed bar, avoiding to make a new request on the frontend just to take it. And on JWT decode, to obtain something like:

{
  "userId": 2,
 "name": "john",
 "loginUser":"johndoe42",
  "iat": 1511030741,
  "exp": 1511117141,
  "aud": "0.0.0.0",
  "iss": "feathers",
  "sub": "anonymous"
}

jwt configuration error

const feathers = require('feathers');
const authentication = require('feathers-authentication');
const jwt = require('@feathersjs/authentication-jwt');
const config = require('./services/config.js');


const app = feathers();

//this two line written above the set up service line 'app.configure(services);'
app.configure(authentication({ secret: config.secret }));
app.configure(jwt());

throw new Error(options.service does not exist.\n\tMake sure you are passing a valid service path or service instance and it is initialized before @feathersjs/authentication-jwt.);
^

Error: options.service does not exist.
Make sure you are passing a valid service path or service instance and it is initialized before @feathersjs/authentication-jwt.
at new JWTVerifier (/home/software/sanjay/80015/PROJECT/subscription/service/node_modules/@feathersjs/authentication-jwt/lib/verifier.js:11:13)
at Function.app.setup (/home/software/sanjay/80015/PROJECT/subscription/service/node_modules/@feathersjs/authentication-jwt/lib/index.js:79:22)
at Function.listen (/home/software/sanjay/80015/PROJECT/subscription/service/node_modules/feathers/lib/application.js:157:10)
at Function.listen (/home/software/sanjay/80015/PROJECT/subscription/service/node_modules/uberproto/lib/proto.js:30:17)
at Object. (/home/software/sanjay/80015/PROJECT/subscription/service/src/index.js:5:20)
at Module._compile (module.js:643:30)
at Object.Module._extensions..js (module.js:654:10)
at Module.load (module.js:556:32)
at tryModuleLoad (module.js:499:12)
at Function.Module._load (module.js:491:3)
at Function.Module.runMain (module.js:684:10)
at startup (bootstrap_node.js:187:16)

"No auth token" with socketio

I'm getting a similar issue to feathersjs-ecosystem/authentication#655 except I'm not using auth0.

app.service('users').patch(userId, data);
// also trying:
app.service('users').patch(userId, data, {
      Authorization: 'Bearer sdfdsfdsfds',
});

With the following transit data in socketio

outgoing:
421["patch","users","5a9acc9562f52c32246526a5",{"username":"abcdef","profile":{"first_name":"FN","last_name":"LN"}},{}]
incoming:
431[{"name":"NotAuthenticated","message":"No auth token","code":401,"className":"not-authenticated","data":{},"errors":{}}]

In neither case do I see the accessToken in transit. Removing the hook authenticate('jwt') from the service works.

I'm thinking it's just not being sent?

Get error in custom verify

Hi there,

is there a reason why you are not passing the error done the verify done function?
I would like to check if the error is a SQL timeout or not so I can send the correct response code.

thanks :)

.catch(error => { debug(Error populating ${this.options.entity} with id ${id}, error); return done(error, {}, payload); }); }

Purpuse of lower casing header option

I'm wondering what is the purpuse of toLowerCase in the code below:

let strategyOptions = merge({
      secretOrKey: jwtSettings.secret,
      jwtFromRequest: ExtractJwt.fromExtractors([
        ExtractJwt.fromAuthHeaderWithScheme('jwt'),
        ExtractJwt.fromAuthHeaderAsBearerToken(),
        ExtractJwt.fromHeader(jwtSettings.header.toLowerCase()),
        ExtractJwt.fromBodyField(jwtSettings.bodyKey)
      ])
    }, jwtSettings.jwt, omit(jwtSettings, ['jwt', 'header', 'secret']));

Restrict authentication by JWT

If you do

app.authenticate({
  strategy: 'jwt',
  accessToken: 'current access token'
});

You get a new access token every time, with new expiresIn etc, so theoretically, you no longer need the users password to know, because you can simply refresh your token before expiration, by this method.

So if a bad guy steals one of my clients jwt, I'm no longer able to block the user account.

If I blacklist the jwt, I need a database lookup. However if the bad guy already refreshed the jwt, I need to know the whole chain, to block every jwt he generated, so I can't simply block the first stolen jwt...

So I can't blacklist him, nor will the jwt ever expire. The only solution would be, to change my servers secret, however, this would invalidate ALL my others client's session.

So should authentication by jwt not simply verify and return the original jwt, and not generate a new one everyone ?

Swallowing errors when getting user from service?

I'm using this plugin with [email protected] and I've run into a confusing situation.

If the storage backing the user service is unavailable, I want my error handler to say "503 Service Unavailable". But if someone tries to authenticate a request with a JWT, the error from the user service gets swallowed and the authentication "succeeds". In my application, the next hook is typically to restrict access by user, so the error handler says "403 Forbidden".

The code in question is in src/verifier.js and there's a test backing it, so that clarifies for me it's not a bug.

I looked at a few other plugins (local, oauth1, oauth2) and they all seem to call out with the error from the service. Why does the JWT plugin behave differently, and would it be ok to make a PR that treats a service error as a failure to authenticate?

Automatically attempt to put the entityId in the payload

I think the server side JWT strategy should try and set up your default payload being the <entity>Id. Currently it does nothing and it's easy to forget to add your own hook to set up the JWT payload. It's also something that you didn't have to do with the previous auth version.

Without the hook your JWT payload is empty, which, when decoded doesn’t populate the entity (because there is no id), which may causes event filters and hooks to not fire, and so on….

It doesn’t error but definitely makes you scratch your head when things don't act as you would expect. So I think it will be super confusing for people.

You would still be able to register your own hooks to modify the payload to your liking but it would default to adding just the entity id.

what's the difference between this and feathers-authentication-local?

In the old auth (0.7.x) when we use type = 'lcoal', it initially verifies with email and password and then depends on jwt payload (params.token and params.user ) to tell which user is logged in. If initially no email and password provided, it will automatically use jwt token in localstorage to verify.

Now in the new auth we have two pacakges, one local and one jwt. Does that mean we have to use both the two to achieve the same functionality?

Using feathers-authentication-jwt for api key authentication

I want to add API keys authentication to my feathesjs app.
It should work by sending a header with a key assigned to the client, which should be checked against a database for validation.

So, if I understand correctly, I could potentially use feathers-authentication-jwt to do this (https://docs.feathersjs.com/api/authentication/jwt.html) by tweaking the configuration and Verifier.

Seems that my problem is that I can't get my Verifier to work and the default one is invoked, so no authentication for you, let me show you.

// authentication.js
'use strict';

const authentication = require('feathers-authentication');
const jwt = require('feathers-authentication-jwt');
const local = require('feathers-authentication-local');
const apikey = require('feathers-authentication-jwt'); // <-- my api key authentication
const Verifier = require('feathers-authentication-jwt').Verifier; // <-- working? apparently yes

console.log('VERIFIER', Verifier); //<-- what is `Verifier`? It's a function

module.exports = function () {
    // do my own verification stuff
	class apiKeyVerifier extends Verifier {
		verify (req, payload, done) {
			console.log('VERIFY');
			done (null, payload);
		}
	}
	const app = this;
	const config = app.get('authentication');

	// Set up authentication with the secret
	app.configure(authentication(config));
	app.configure(jwt());
	app.configure(local());
	console.log('API VERIFIER', apiKeyVerifier); // <-- what is `apiKeyVerifier`? a class
	app.configure(apikey({
		name: 'apikey',
		service: 'api-keys',
		entity: 'api-keys',
		idField: 'api_keys_id',
		header: 'x-api-key',
		Verifier: apiKeyVerifier
	}));

	// The `authentication` service is used to create a JWT.
	// The before `create` hook registers strategies that can be used
	// to create a new valid JWT (e.g. local or oauth2)
	app.service('authentication').hooks({
		before: {
			create: [
				authentication.hooks.authenticate(config.strategies)
			],
			remove: [
				authentication.hooks.authenticate('jwt')
			]
		}
	});
};

What happens when I try to authorize?
I try like this:

curl -X POST \
  http://localhost:5000/authentication \
  -H 'cache-control: no-cache' \
  -H 'content-type: application/x-www-form-urlencoded' \
  -H 'x-api-key: &xh&vtK9s¿Gr<JG' \
  -d strategy=apikey

I get this response:

feathers-authentication:passport:authenticate Authentication strategy 'apikey' failed { JsonWebTokenError: jwt malformed
      at Object.module.exports [as verify] (/Users/transistor/Documents/WebServer/asc-api/node_modules/jsonwebtoken/verify.js:54:17)
      at Function.module.exports [as JwtVerifier] (/Users/transistor/Documents/WebServer/asc-api/node_modules/passport-jwt/lib/verify_jwt.js:4:16)
      at JwtStrategy.authenticate (/Users/transistor/Documents/WebServer/asc-api/node_modules/passport-jwt/lib/strategy.js:91:17)
      at /Users/transistor/Documents/WebServer/asc-api/node_modules/feathers-authentication/lib/passport/authenticate.js:131:18
      at /Users/transistor/Documents/WebServer/asc-api/node_modules/feathers-authentication/lib/passport/authenticate.js:38:14
      at Object.<anonymous> (/Users/transistor/Documents/WebServer/asc-api/node_modules/feathers-authentication/lib/hooks/authenticate.js:80:55)
      at process._tickCallback (internal/process/next_tick.js:103:7) name: 'JsonWebTokenError', message: 'jwt malformed' } undefined
info: error: authentication - Method: create: jwt malformed
error:  NotAuthenticated: jwt malformed
      at NotAuthenticated.ExtendableBuiltin (/Users/transistor/Documents/WebServer/asc-api/node_modules/feathers-errors/lib/index.js:21:28)
      at NotAuthenticated.FeathersError (/Users/transistor/Documents/WebServer/asc-api/node_modules/feathers-errors/lib/index.js:96:116)
      at new NotAuthenticated (/Users/transistor/Documents/WebServer/asc-api/node_modules/feathers-errors/lib/index.js:149:117)
      at /Users/transistor/Documents/WebServer/asc-api/node_modules/feathers-authentication/lib/hooks/authenticate.js:102:31
      at process._tickCallback (internal/process/next_tick.js:103:7)

Which, if I'm not mistaken, it means that my Verifier is not being used, so it fails because it isn't a jwt.

So, first, am I on the right track? I mean, can I use this strategy to authenticate by API keys?

Second, if so, what am I doing wrong?

Thanks!

Problem with "header" configuration. Collision of config options?

I have a configuration in place exactly like in the docs at https://docs.feathersjs.com/api/authentication/server.html

and feathers-authentication-jwt complains at https://github.com/feathersjs/feathers-authentication-jwt/blob/master/src/index.js#L40
that "header": { "type": "access" } may not be an object.

excerpt from config:

  "authentication": {
    "header": "Authorization",  
    "secret": "...",
    "jwt": {
      "header": { "type": "access" },
      "audience": "mydomain.de",
      "subject": "access",
      "issuer": "mydomain.de",
      "algorithm": "HS256",
      "expiresIn": "14d"
    },

I think there is a collision of the header configurations being merged somehow?

My project is pretty new set up with feathers-generate.

How to extract token from request ?

Is there a better way than that to get the token from the request?
Or ideally recover the user without protecting the route as does the express middleware?

req.feathers.token = app.passport._strategies.jwt._jwtFromRequest(req);
Example
  app.use('/graphql',
    (req, res, next) => {
      req.feathers.token = app.passport._strategies.jwt._jwtFromRequest(req);
      next();
    },
    graphqlExpress(({ feathers }) => ({
      schema,
      context: {
        app,
        feathers
      }
    }))
  );

Cannot authenticate using feathers token

app runs smoothly on my local but after deploying my app to server, calling feathersApp.authenticate() returns error:

Bad "options.jwtid" option. The payload already has an "jti" property.

Add docs section on expected request params

We should probably add a section to the README.md on what params the Strategy is expecting to receive.

Expected Request Data

By default, this strategy expects a payload in this format:

{
  strategy: 'jwt',
  <whateverGoesHere>: <token>
}

Add support for 'Bearer token' style header

It should also probably be the first priority. It's as simple as changing these lines to these:

jwtFromRequest: ExtractJwt.fromExtractors([
    ExtractJwt.fromAuthHeader()
    ExtractJwt.fromHeader(jwtSettings.header.toLowerCase()),
    ExtractJwt.fromBodyField(jwtSettings.bodyKey)
])

and writing a quick test.

Add support for dynamic secrets from passport-jwt

passport-jwt is preparing (mikenicholson/passport-jwt#108) the support for dynamic secrets provided by a function which is useful for tokens signed with asymmetric algorithms used with JWKS endpoints and key rotation (see https://auth0.com/blog/navigating-rs256-and-jwks/ for an example).

They will support it by adding a new config property secretOrKeyProvider that will accept a function and can be used instead of the existing secretOrKey property. Either secretOrKey or secretOrKeyProvider must be provided. Both must not be provided. The updated README: https://github.com/themikenicholson/passport-jwt/tree/pr108-secret-key-provider

This is not usable through feathers-authentication-jwt because it requires a secret property that it then maps to secretOrKey and fails if no secret is provided.

It would be great to add support for that option in feathers-authentication-jwt. This could be done by supporting an extra property that will be map to secretOrKeyProvider if it's present or by simply not required the secret key to be provided to feathers-authentication-jwt and only map it if it's present.

Re-using the same JWT with Primus / websockets

I've bumped Feathers.js to v1.0.0-beta-2 locally, and have managed to implement a working 'local' strategy for pulling user data from a 'users' table, using feathers-knex.

I get the token back locally, and can use .verifyJWT() and .service('users').get(payload.userId) to retrieve the user. A call to .getJWT() locally confirms it's being stored properly in localStorage.

So far, so good.

The problem I'm having is re-using that JWT token on subsequent requests.

Calling app.authenticate() yields another JWT, but without userId being attached to the payload. It looks like it's ignoring the locally stored JWT and requesting a blank new one.

Calling app.authenticate({strategy: 'jwt', token: '...'}) yields the following error:

NotAuthenticated: No auth token

(I've tried 'token', 'jwt' and various other formats to pass in that token manually in the request... but I assume since the server is using the default ExtractJwt.fromHeader, this probably has no effect).

I'm wondering whether this has something to do with the fact I'm using Primus with websockets in my authentication request, which is screwing with the format of the request? Do I have to do something special to get the token from localStorage and bolt it on to the request somehow?

In your feathers-passport-jwt package (I can't tell whether it's deprecated or not; I'm not using it locally), it mentions this implementation:

socket = io('', {
// Assuming you've already saved a token to localStorage.
query: 'token=' + localStorage.getItem('featherstoken'),
transports: ['websocket'], // optional, see below
forceNew:true, // optional, see below
});

However, I note from your feathers-authentication migration doc this line:

The JWT is only parsed from the header and body by default now. It is no longer pulled from the query string unless you explicitly tell feathers-authentication-jwt to do so.

... so I'm not sure exactly what to do.

Could you provide an example please for re-using the JWT using Primus?

JWT verifier: do not return 404 error on not found identity

Hi there,

I think that the verifier should not return the error on 404 finding a missing identity. The use case:

  1. get a valid token with a valid user
  2. get a request for an endpoint with this token -> ok
  3. delete the user
  4. make a new request with the same token -> 404

I think that the verifier should return 401 with Unknown user.
It is a wrong though?
All the best!

Verifier is not called in a custom service route

I built a JWT verifier that just to make some user checks:

//Verifier.js
module.exports = class Verifier extends jwt.Verifier {
  verify(req, payload, done) {
    const verification = this
    const app = verification.app
    Promise
      .resolve(app)
      .then(app => Awesome.check(app,payload.id))
      .asCallback((err,user) =>{
        return done(err, user, payload)
      })
  }
}

//authentication.js
//...
  const config = app.get('authentication')

  app.configure(authentication(config))
  app.configure(jwt({
    Verifier: Verifier,
    service: 'users'
  }))
  app.configure(local(config.local))
//...

Everything works fine for all services and all the before hooks contains the user's auth payload.

But, I have this particular custom endpoint, that is just a particular case of my service 'commands' that the user doesn't need to choose the 'commandTypeId':

//commands.service.js
  app.use('/commands', createService(options))
  app.use('/commands/shutdown', {
    create: (data, params) => {
      data.commandTypeId = 1 //id of shutdown command type
      return app.service('commands').create(data)
    }
  })

In this case, the verifier is not called and has no user auth payload. I could check with the (before) hook.params is just empty.

There is my package.json dependencies . I created the app with feathers-cli.

"dependencies": {
    "bluebird": "^3.5.0",
    "body-parser": "^1.17.2",
    "compression": "^1.6.2",
    "cors": "^2.8.3",
    "feathers": "^2.1.3",
    "feathers-authentication": "^1.2.4",
    "feathers-authentication-hooks": "^0.1.4",
    "feathers-authentication-jwt": "^0.3.1",
    "feathers-authentication-local": "^0.3.4",
    "feathers-authentication-oauth2": "^0.2.4",
    "feathers-configuration": "^0.4.1",
    "feathers-errors": "^2.8.1",
    "feathers-hooks": "^2.0.1",
    "feathers-hooks-common": "^3.5.5",
    "feathers-rest": "^1.7.3",
    "feathers-sequelize": "^2.0.1",
    "helmet": "^3.6.1",
    "knex": "^0.13.0",
    "lodash": "^4.17.4",
    "moment-timezone": "^0.5.13",
    "passport-google-oauth20": "^1.0.0",
    "pg": "^6.3.1",
    "randomstring": "^1.1.5",
    "sequelize": "^4.1.0",
    "serve-favicon": "^2.4.3",
    "validator-js": "^0.2.1",
    "winston": "^2.3.1"
  },
  "devDependencies": {
    "async": "^2.4.1",
    "eslint": "^3.19.0",
    "mocha": "^3.4.1",
    "request": "^2.81.0",
    "request-promise": "^4.2.1"
  }

Need I to do something to use the Verifier in this context?

Thanks in advance.

Clean generated project throws Error: You must provide a 'header' in your authentication configuration

When generating a clean project, with v2.3.6 of the cli - i.e.

>feathers generate app
>feathers g authentication
>yarn start

you get the issue Error: You must provide a 'header' in your authentication configuration or pass one explicitly, here is the full log you get:

yarn start v1.0.1
$ node src/
You are using the default filter for the users service. For more information about event filters see https://docs.feathersjs.com/api/events.html#event-filtering
C:\Users\George\Source\Repos\ts4\node_modules\feathers-authentication-jwt\lib\index.js:58
      throw new Error('You must provide a \'header\' in your authentication configuration or pass one explicitly');
      ^

Error: You must provide a 'header' in your authentication configuration or pass one explicitly
    at EventEmitter.jwtAuth (C:\Users\George\Source\Repos\ts4\node_modules\feathers-authentication-jwt\lib\index.js:58:13)
    at EventEmitter.configure (C:\Users\George\Source\Repos\ts4\node_modules\feathers\lib\application.js:150:8)
    at EventEmitter.module.exports (C:\Users\George\Source\Repos\ts4\src\authentication.js:12:7)
    at EventEmitter.configure (C:\Users\George\Source\Repos\ts4\node_modules\feathers\lib\application.js:150:8)
    at Object.<anonymous> (C:\Users\George\Source\Repos\ts4\src\app.js:44:5)
    at Module._compile (module.js:570:32)
    at Object.Module._extensions..js (module.js:579:10)
    at Module.load (module.js:487:32)
    at tryModuleLoad (module.js:446:12)
    at Function.Module._load (module.js:438:3)
error Command failed with exit code 1.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.