Code Monkey home page Code Monkey logo

supertokens-website's Introduction

supertokens-website's People

Contributors

alisher-aituarov avatar anku255 avatar bhumilsarvaiya avatar dependabot[bot] avatar dulowski-marek avatar makeitcount avatar nkshah2 avatar porcellus avatar prateek3255 avatar rishabhpoddar avatar sattvikc avatar seniorquico avatar sublimator avatar sudiptog81 avatar vaibssingh avatar xuatz 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  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  avatar  avatar

supertokens-website's Issues

Add test for `doesSessionExist` returning false when backend is not reachable

Problem

Currently (supertokens/supertokens-auth-react@1c9f5b3) supertokens-auth-react relies on doesSessionExist returning false in SessionAuth when backend is not reachable.

Goal

Preserve the bevaiour to keep the developer experience good. It should still render the UI when backend is unreachable, because we instruct the user to setup the frontend first.

Solution

Add a unit test:

  1. Given that backend is not reachable
  2. When I call doesSessionExist
  3. Then it should return false and NOT throw an error

Note: doesSessionExist calls refresh session function underneath, so make sure to test for this case as well.

Prerequisites

This will require unit tests to be set up.

Allow for two independent sessions to exist across sub domains.

Describe the solution you'd like
If I have a session on example.com, I should be able to have another, unrelated session on a.example.com

Additional context
If my main website is example.com, and I have a test site on test.example.com, I want to be able to test sessions in both of them. Right now, that is possible as long as I use different browsers for each (or incognito). But if I am logged into example.com and I open test.example.com in the same browser, then I cannot test sessions on test.example.com

Unnecessary refresh call if an API returns 401 with id-refresh-token header as remove?

I noticed (in the golang with-twirp example), that if the API revokes a session and then calls sessionContainer.GetSessionData(), the API eventually returns a 401, unauthorised error.

The headers in this API call are as expected - the id-refresh-token header value is "remove" and the set-cookie headers remove the session tokens.

However, on the client side, we observe that it calls the refresh endpoint immediately after (which then yields a 401 too). This refresh endpoint should not be called since the frontend knows that the session has been revoked.

Perhaps this is an issue with the backend and not the frontend.. not sure.


There also seems to be an issue with fetch doing an unnecessary refresh API call when there are no frontend cookies set, and the actual API to call returns a 500 error. In this case, it does a refresh call, then the actual APi call (which fails), and then a refresh call again. But I think that this happens only if both te refresh calls return a 500 error as well.

Possible reasons why session doesn't work (for debugging user's app)

When using cookie based auth

  • No supertokens.init called
  • apiDomain set on the frontend doesn't match the actual API domain - so the interceptor is not called.
  • Access-Control-Expose-Headers not set properly - which prevents frontend from reading the id-refresh-token, which prevents setting that state on the frontend.
  • Access-Control-Expose-Headers set to * - which prevents frontend from reading the front-token, which prevents setting that state on the frontend. Even though it's star, it won't work cause of using credentials.
  • sameSite is none, but user set the cookie secure flag to false.
  • Access control allow headers not set properly, failing CORS
  • anti-csrf not being provided (either token if that is the mode, or custom header)
  • Issues with iframes
  • API and website domain do not share the same TLD (for example API is on api.example.com and website is not on *.example.com and not on example.com), so Safari will not send cookies even if sameSite is none. Switch to using header based auth.
  • See supertokens/supertokens-core#280
  • Access-Control-Allow-Origin set to * which prevents browser from using setCredneitials: true. And if setCrednetials is false, then cookies are not sent.
  • Imported from different recipe by mistake.
  • Custom interceptor added which messes up refresh APIs. If using axios, they should add their interceptor AFTER both addAxiosInterceptor is called and after supertokens.init is called, this way, even if they are breaking the axios interface, it will still all work
  • Cookies have secure attribute in them, but the API domain being queried is HTTP
  • User's reverse proxy has a size limit on the response payload / header (can be configured on nginx). This in turn causes the access token from not being sent to the frontend, and the frontend getting a 502 (or another 5xx) error.
  • Session being created via an API that is not called by the frontend of the user's app. This can happen in case they are using some third party auth provider that calls their API webhook directly.
  • Using node-fetch instead of browser fetch even on client side. People might be using node-fetch for server side rendering and then not realising that they are using that for client as well. Our interceptors are added to window.fetch so their node-fetch wouldn't get the interceptors added, preventing a call to the refresh API.
  • Using sveltekit's fetch instead of window.fetch
  • Calling an APi from server side rendering instead of from the client.
  • Sometimes, CORS can be a problem during development. To solve this issue, you can ask the user to disable CORS on their browser using https://alfilatov.com/posts/run-chrome-without-cors/
  • Access control allow credentials may be false preventing cookies from being sent. This can happen if the frontend interception is not happening, or if the user has manually set that header to false.
  • Session.init not called, or being called after using axios / fetch (even though interceptor is being added)
  • websiteDomain is set to 127.0.0.1 but website is being loaded on the browser using localhost (or vice versa).
  • document.cookie doesn't work. This usually happens in case of electron apps or some other browser emulation tech. In this case, we see infinite refreshes being called. To solve this, we should ask the user to pass their own cookieHandler in the init function call
  • Have a proxy in the middle of frontend and backend which strips away necessary headers like cookies. This causes weird behaviour where refresh might return unauthorised and the frontend would clear frontend set cookies, but the backend sAccessToken and stuff still remains..
  • sessionScope not correctly set yields sIRTFrontend not being set even after session refreshing returns 401 on first visit.
  • cookieDomain not correctly set which yiels access token and others not being attached to apiDomain
  • On the frontend, websiteBasePath is set to an incorrect value. This leads the auth page to navigate to some other page than what is expected
  • For domains like heroku, if the website domain and api domain are not equal or not a sub domain (disregard the .herokuapp.com common part), then same site has to be set to none. Note that this will not work on safari. Switch to using header based auth.
  • User is using another session management solution like django's built in which returns 401 causing refresh session to be called which returns 200, and then it goes in an infinite loop.
  • User is using auto generated client code (from swagger for example), which also seems to add a fetch interceptor. That fetch interceptor probably runs after ours, and if the user gives credentials: "same-origin", to the headers for that lib, the cookies won't be sent.
  • In react native the user may have either accidentally used CookieManager.clearAllCookies or they might be manually adding cookies while replacing existing ones.
  • The user has explicitly set access-control-expose-headers and their values are incorrect. Their headers override ours. This can cause an issue in which the access token and stuff gets set, but the sIRTFrontend and frontToken on the cookie don't get set.
  • The apiDomain and websiteDomain are on a shared domain (like azurewebsite.net) and the api and website are diffrent sub domains of this - but our SDK doesn't recognise that this is a shared domain and sets the cookie same site to lax, even though it should be none.
  • Changing of cookie domain value can lead to older session tokens (refresh token) still lingering around in the browser, and if that refresh token gets picked up during session refreshing, then refreshing will always fail - even for new logins. The solution is to clear all cookies in the browser.
  • If verifySession returns 403, and the response contains claimValidationErrors: id: st-ev, this means that the user needs to go through the email verification flow
  • Changing cookieDomain on the backend whilst a session exists may lead to having two sAccessTokens which will cause issues like the session not being verified correctly. The solution here is to manually clear the cookies and relogin.

Documentation / Implementation mismatch

In contradiction to the API docs, which say that sessionPossiblyExists returns a boolean, it actually returns a Promise.

Not only did this trip me up as I didn't expect it from reading the docs, it also seems to complicate things unnecessarily since the sessionPossiblyExists method does not actually do anything asynchronous. I'd suggest removing the async from the method.

Use another storage to store anti-csrf

Using cookies will:

  • Allow subdomains to share a session without having to call refresh again and again each time the user switches to a different subdomain
  • Is not a security issue since an anti-csrf token is still expected to be in the anti-csrf header.

Add tests for reading userId and JWT payload from the frontend

  • Create a session check that userId is there on the frontend
  • Refresh a session and make sure JWT payload is accessible
  • Access the JWT payload when the access token is expired and make sure that refresh is called once - this test is commented "refresh session via reading of frontend info".
  • When the session is removed, check that the front-token is also removed
  • Update JWT payload and check that frontend token is also updated
  • Create a session with some custom JWT payload, and see if that's accessible on the frontend
  • Changing on jwt payload with blacklisting on, reflects the change immediately vs with refresh, it only reflects after a refresh call
  • Resolve TODO from test "test session should not exist when user calls log out" and "test with fetch session should not exist when user calls log out".
  • Uncomment "refresh session via reading of frontend info" and "refresh session via reading of frontend info using fetch"
  • Resolve TODOs in "update jwt data" and "test update jwt data with fetch", after all drivers have implementation.

Bug: SuperTokens website url nomalisation does not work for electron apps

Summary

When using supertokens-website with Electron and running in production mode, supertokens-website throws an error Error: Please provide a valid domain name.

This is because in production mode electron return an empty string for window.location.origin which the SDK relies on.

Expected behaviour

supertokens-website should not throw an error when using with Electron

%s/let/const/g

Hi there!

I do not want to fill out a form to contribute - so just a quick hint.

In the NodeJs docs: https://supertokens.io/docs/nodejs/installation

You can/should replace all occurrences of let with const - as far as I can see you do never assign a new value to the variables in question. Your app will still run fine - it propagates bad habits though ;).

[Discussion] Custom Cookie and Window API Handlers

Summary

SuperTokens frontend SDKs use local storage and frontend cookies to store information. The problem is that some web frameworks (Electron, Capacitor etc) have a very different way of handling cookies and storage.

Cookies

For example, Electron apps have a separate layer for the actual frontend application and another layer for Electron functions. When setting frontend cookies these cookies are set to the electron layer and not the JS/Frontend layer, which means that when the SDK tries to read values from document.cookie, nothing is returned.

To solve this problem, the SDK exposes a way to set custom cookie handlers that are used by the SDK instead of referring to document.cookie directly. In the case of electron this handler can be used to get/set cookies from/to local storage.

In the case of Capacitor this is not a complete solution because:

  1. localstorage is not considered a reliable way of storing long lasting information
  2. The recommended way of using storage can only be used asynchronously

Storage

For example in Capacitor even though window.localstorage is available, it is not recommended to be used for long lasting information because the OS periodically clears it. The framework provides its own Storage API but only allows for async operations.

To solve this the SDK exposes a way to set custom handlers for localstorage and sessionstorage that will be used instead of accessing window.localstorage and window.sessionstorage directly.

An additional problem is that the SDK in specific cases reads from localstorage in the UI layer (which means it cannot be done asynchronously), to solve this the SDK allows storage to be handled in async and sync ways (using two sets of functions).

At the time of writing this is not a problem with Capacitor because the information that the SDK needs is not designed to be long lasting and there are no serious side effects if the OS clears the data. This consideration is not applicable to other frameworks because localstorage works as expected.

Final Interface

The final interface looks like this:

Storage

    key: (index: number) => Promise<string | null>;
    getItem: (key: string) => Promise<string | null>;
    clear: () => Promise<void>;
    removeItem: (key: string) => Promise<void>;
    setItem: (key: string, value: string) => Promise<void>;
    /**
     * Sync versions of the storage functions
     */
    keySync: (index: number) => string | null;
    getItemSync: (key: string) => string | null;
    clearSync: () => void;
    removeItemSync: (key: string) => void;
    setItemSync: (key: string, value: string) => void;

Cookies

    setCookie: (cookieString: string) => Promise<void>;
    getCookie: () => Promise<string>;

API returning a 401 error can lead to Axios infinite request loop

Hi there ๐Ÿ‘‹,

First, I want to say I'm loving the library so far - as someone who is relatively new to coding, this makes it easy (and even fun!) to implement authentication, and I really appreciate it. Thanks so much for all your hard work on this!

Bug description

I'm using the Axios interceptor in a client-side React app to make requests to my Fastify server (where Supertokens is set up).

So, I've identified a bug where if a user is already signed in and has a current session, and then they make a request to my API and receive a 401 error, the Axios interceptor assumes that the session is expired and automatically sends a request to refresh the session. But in my case, the user already has a current session, and my API is returning a 401 error for an entirely different reason. So the client keeps trying the /session/refresh route (which is successful) and then re-trying my API route (which returns 401), and this becomes an infinite loop. (I can see this in the Network tab of Chrome Dev tools)

There are several ways for me to work around this, the easiest of which is to just have my API route return a different error code than 401. But I'm pretty sure this is a bug that needs to be addressed, so as to avoid the loop!

Steps to reproduce the issue

  1. Set up an API route protected by the verifySession middleware (i'm using Fastify, but it can be any Node server). The route returns a 401 error even after the session is verified through verifySession
  2. On the client side, set up Supertokens and Axios interceptor. Sign in a user, and then make a request to the API route through Axios

I'd be happy to put together a more concrete example if needed. Let me know!

Code that may be the issue

I have a hunch that this happens because of one of the status code checks below (or a similar one somewhere else), where it's only checking the response.status code of the response, and so it automatically assumes that the session needs to be refreshed if the API returns a 401 error. But if the user already has a current session at this point, and it keeps getting a 401 error from an API route - this results in the loop. Perhaps some code could be added here to make sure that the 401 error is actually happening because of an expired session?

if (response.status === AuthHttpRequestFetch.config.sessionExpiredStatusCode) {
let config = response.config;
return AuthHttpRequest.doRequest(
(config: AxiosRequestConfig) => {
// we create an instance since we don't want to intercept this.
// const instance = axios.create();
// return instance(config);
return axiosInstance(config);
},
config,
url,
response,
true
);

if (response.status === AuthHttpRequestFetch.config.sessionExpiredStatusCode) {
const refreshResult = await onUnauthorisedResponse(preRequestIdToken);
if (refreshResult.result !== "RETRY") {
// Returning refreshResult.error as an Axios Error if we attempted a refresh
// Returning the response to the original response as an error if we did not attempt refreshing
returnObj = refreshResult.error
? await createAxiosErrorFromFetchResp(refreshResult.error)
: await createAxiosErrorFromAxiosResp(response);
break;
}

Use window object in init function only if necessary for SSR

There is no window object when app is rendered server side (SSR)
See https://stackoverflow.com/questions/55151041/window-is-not-defined-in-next-js-react-app

Server side rendering using NextJS is impossible for supertokens-auth-react and/or for supertokens-website because of the following line which we could easily optimise

let sessionScope = normaliseSessionScopeOrThrowError(window.location.hostname);

Currently, using SuperTokens with NextJS is suboptimal as we need SuperTokens to be initialised to render logged in or authentication components, but we can only initialise SuperTokens on the client side. This defeats the purpose of using NextJS for SSR.

By making the use of window object optional (if else statement on sessionScope object), we could init SuperTokens completely on the server side.

Note: This is the error that the app encounter during init but there might be others, I haven't checked all use of window object in Session.init() yet

README improvements

The current readme does not give a good overview of the project and does not guide the reader properly to use it.
Some changes, that will fix it

  • Screenshots of what user will be able to build with this library
  • Installation code snippets
  • Usage code snippets
  • Visuals to show how frontend connect with the auth backend
  • Link to demo
  • Supertokens architecture/flow diagram
  • Better contextual links to docs/resources

how to support SWR? even urql of graphql?

hi, this pkg container addAxiosInterceptors func to support axios, but if i use swr, how to auto fix the cookie items? maybe some doc to tell us to how to do. so if i want to use urlq for graphql queries, i can do by myself.

Do not call recipe impl function inside another function that is unable to supply a user context to it

For example, getAccessTokenPayloadSecurely calls doesSessionExist internally. So if someone overrides doesSessionExist and in that calls getAccessTokenPayloadSecurely, it leads to an infinite loop. This can be solved by providing appropriate user context values and breaking the infinite recursion loop.

However, there is a case where refreshing a session calls recipeImpl.doesSessionExist which then if it calls getAccessTokenPayloadSecurely, which can also refresh the session. This causes an infinite recursive loop where the user context is no longer passed and cannot be used to break the loop.

Allow session refreshing even if the frontend thinks that the session doesn't exist

Ths need for this comes when someone is using us only for session recipe and another auth provider for login. In this case, the other auth provider would do a callback to this user's API layer directly which would create a new supertokens session.

In this case, the httpOnly session cookies would be correctly set, but since there are no frontend interceptors (since the auth provider made this API call directly), the frontend would either:

  1. Not have sIRTFrontend
  2. Have sIRTFrontend with the value of remove.

In case of (1), when the frontend would call doesSessionExist, it would call the refresh API and it would all work fine. But in case of (2), doesSessionExist would return false. The only way to then make the frontend realise that the session actually exists is to manually clear the sIRTFrontend cookie and then call doesSessionExist.

So we should expose a function that does this - something like doesSessionExistCheckUsingBackendCall, and then document that accordingly.

Token refreshing is not working with @nuxtjs/axios (401 response)

Using
Nuxt.js - 2.15.8
@nuxtjs/axios - 5.13.6
SuperTokens-website - 10.0.5
SuperTokens-node - 8.2.0

Problem
Creating Axios instance from @nuxtjs/axios and adding supertoken Interceptor is breaking, causing token refreshing to work incorrectly. Interceptors are getting added after first request.

Code
/plugins/axios.js

import SuperTokens from 'supertokens-website';

export default function ({ $axios, $config }, inject) {
  // Create a custom axios instance
  const axiosInstance = $axios.create({
    baseURL: $config.root,
  });

  // Add SuperTokens interceptors
  SuperTokens.addAxiosInterceptors(axiosInstance);

  // Inject to context as $axios
  inject('axios', axiosInstance);
} 

nuxt.config.js

plugins: ['~/plugins/axios']

Solution for users
Using Axios from Axios package (not from @nuxtjs/axios) and injecting it into Nuxt.js context fixes the issue.

SessionScope fails on Electron

let sessionScope = normaliseSessionScopeOrThrowError(getWindowOrThrow().location.hostname);
if (options.sessionScope !== undefined) {
    sessionScope = normaliseSessionScopeOrThrowError(options.sessionScope);
}

On Electron getWindowOrThrow().location.hostname returns an empty string and that fails the check. Even if I have set the sessionScope in config the location takes precedence. Would be nice for the config to be the one taken into account first.

const sessionScope = normaliseSessionScopeOrThrowError(options?.sessionScope || getWindowOrThrow().location.hostname);

If you're ok with it I can make a PR.

Cross-site: document.cookie sIdRefreshToken not accessible causing anti-csrf to be removed

Cross-site Cookies

After updating the server with cors middleware (options = { origin: true, credentials: true }) and front-end axios requests withCredentials:true, session cookies are properly received/sent from/to the server however the response sIdRefreshToken cookie is not accessible via document.cookies. This causes the anti-csrf token to be removed in the axios response interceptor.

Environment

  • version of supertokens-website - 3.2.9
  • version of supertokens-node-mysql-ref-jwt - 4.4.0
  • version of node - v10.11.0
  • version of mysql - 5.7

Related code

if (getIDFromCookie() === undefined) {

Other information

Even after this issue is resolved, the refresh endpoint would not work after the cookie has expired. I believe the solution may be to pass the sIdRefreshToken in a new response header instead of as a cookie and store it in the browser's localStorage instead.

Is UNAUTHORISED event firing correctly?

An instance of when we fire them is if after getting the refresh lock, and if the session doesn't exist, then we fire it. This may not be correct (see fetch.ts, line 357)

Add test for deleting cookies when core returns "UNAUTHORISED" response on call to refresh session

In our C# backend implementation, we missed a call to delete the frontend cookies when the core returned an UNAUTHORISED response when the frontend was calling the refresh session endpoint on the backend. It would be awesome to have a test for this specific scenario, as it's quite difficult for us to reproduce within the backend itself (depends on an UNAUTHORISED response from the core).

Use with RTK Query - unable to assign request headers (specifically `Content-Type` and `rid`)

Edit: this does pertain to ST's implementation of a fetch wrapper.

Possibly out of scope of ST since this is for use with a third-party library (Redux Toolkit (RTK) Query), please close if so.

As described in this RTK issue: reduxjs/redux-toolkit#2205 (comment):

The expected behavior is the headers should be included in the request if included in any of the ways described (using prepareHeaders, assigning object literal to headers, or assigning the header within the header key in an endpoint's query).

The observed behavior headers are not able to be assigned (including rid and Content-Type to HTTP requests made with fetchBaseQuery (see https://redux-toolkit.js.org/rtk-query/api/fetchBaseQuery#using-fetchbasequery) w/SuperTokens. This results in the request body not being parsed/sent correctly.

Thought I would open here since RTK Query is gaining traction, thanks!

What is the session behaviour if multiple distinct (a.com & b.com) sites use the same session?

Some example issues:

  • if a.com is open in one tab, and b.com in another, then synchronising refresh API calls is not possible using cookies nor localstorage...
  • If a user logs in via a.com, the API domain has cookies set against them. Now if the user logs into b.com, the cookies in the APIs are over written. What happens to a.com's session?
  • If user logs out of a.com, then b.com might still think that the user is logged in..

Is verifySession making a call to CORE server each time?

I'm considering moving all auth to SuperTokens self hosted core.

Now my question is if i use verifySession() that guards my user's data API
is it making additional call to core server evertime?

Basically i see that there are 3 servers involved - front, back and core.

My goal would be to Log In user on front end by calling core
and then just call backend server (for users data) directly from front end.
(Without calling core server on each backend verifySession() )

Is this how SDK works? Or every time i use verifySession() , a call to core will be made?

Longer explanation: I consider using two Heroku dynos one for core and one for backend,
with CloudFlare worker for serving frontend.

If i would make two calls (back & core) for each API call,that would slow down
app and would increase time to get data to user by ~50%.
(And would lessen the worth of having serverless app in first place )

Changes due to cookie (that are set on the client side) expiration capped to 7 days

  • On browsers like safari and brave, cookies set on the client side are caped to 7 days expiry (https://www.cookiestatus.com/)
  • This means that tokens like frontToken, or idrefreshtoken, or anticsrftoken that are set in cookies will be deleted after 7 days.
  • We can use localstorage instead, but this means that working across sub domains will not be possible:
    • We can create an API on the backend that says if a session exists or not. If it does, then it will set these tokens in localstorage. But this has an issue that if these tokens are deleted from one sub domain's localstorage, they will persist in the next sub domain's localstorage (even if the session doesn't exist).
    • In case the user is using an anti-csrf token, then the new suddomain cannot get that token at all.
  • We can continue to use cookies and also have an API in case no cookies exist (after 7 days)
    • This will work well, as long as the user is not using anti-csrf tokens, since after 7 days, that anti-csrf token will be missing, and can't be recovered -> which will yield a logout.

Finally, we decided to go with an iframe method that will allow us to share localstorage across sub domains.


TODO:

  • Changes to supertokens-website
  • Changes to auth-react
  • Changes to docs
    • function sign change to getUserId and to doesSessionExist (they both are now async)
    • context usage
    • requireAuth boolean in Auth wrapper and session wrapper

(Easy) Allow the user to give just the API URL in `init` function call

I don't think this change is a necessary since it adds confusion in terms of communication in docs. For example, the user doesn't pass the refresh URL anymore, so the param cannot be called refreshTokenUrl, so we can raname it to apiEndpoint. But, in case they need to pass a custom refresh path, then they will be passing the whole URL, in which case, apiEndpoint is not a good name.

The init function call can be seen here for fetch and here for axios.

As of now, the refreshTokenUrl must be a full path to the user's refresh API endpoint. Instead, we need to change it so that if they give:

  • https://api.example.com or https://api.example.com/, this line assigns https://api.example.com/session/refresh to AuthHttpRequest.refreshTokenUrl, and similarly for index.ts

  • https://api.example.com/some/other/path, this line assigns https://api.example.com/some/other/path to AuthHttpRequest.refreshTokenUrl, and similarly for index.ts.

Essentially, we want them to just give the API domain for ease of use.

Important: We should also change the parameter name from refreshTokenUrl to apiEndpoint since that is more accurate now.

This issue will also require to write some tests for axios and for fetch which check that the right URL is assigned given various inputs. You could create a new function in axios.ts and index.ts to return the value of refreshTokenUrl that was assigned.

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.