GithubHelp home page GithubHelp logo

smart-launcher's Introduction

SMART/FHIR proxy server and app launcher

Launcher for SMART apps

Installation

Make sure you have Git and NodeJS 16 or higher, and then run:

git clone https://github.com/smart-on-fhir/smart-launcher.git
cd smart-launcher
npm i

Usage

You can use existing NPM scripts from within the project folder for common tasks:

Start the server

npm start

Note that this will fail until you create a .env file with some required settings. Minimal example (only the required settings):

FHIR_SERVER_R2="https://r2.smarthealthit.org"
FHIR_SERVER_R3="https://r3.smarthealthit.org"
FHIR_SERVER_R4="https://r4.smarthealthit.org"
PICKER_CONFIG_R2="r2"
PICKER_CONFIG_R3="r3"
PICKER_CONFIG_R4="r4"

Test

To run the tests execute

npm test

# or this to also generate a coverage report
npm run test:cover

Develop

If you want to modify something run

npm run dev

This will watch for changes and restart the server automatically. It will also run the tests on every change.

OIDC Keys generation

To generate new private and public keys make sure you have openssl (comes pre-installed on Mac), cd to the project root and execute:

npm run cert

Then re-start the server and it will use the new keys.

Notes about jwt.io

People often use https://jwt.io/ to generate and validate tokens. However, it seems that the RS256 signature verification feature expects you to paste x.509 formatted public key or certificate and does not work with PEM-encoded PKCS#1 public keys. For that reason, if you want to manually verify your token at https://jwt.io/, you will need to provide the original x.509 version of the public key that you can find at the /public_key endpoint of the server.

Using Docker

docker run -t -p 9009:80 smartonfhir/smart-launcher:latest

smart-launcher's People

Contributors

davidvaccaro avatar dependabot[bot] avatar gotdan avatar holmesie avatar james-ingold avatar jmandel avatar kherock avatar vlad-ignatov 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

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

smart-launcher's Issues

jwt error with access token signed in RS256, JsonWebTokenError: invalid algorithm

Hi All, i'm having problem to authenticate with an access token signed with RS256 created by identityserver4. Checking the code seems that is only managed token signed with symmetric key, could you please tell me why? it's a limit that in my case because i have to use identityserver4 to authenticate my EHR system and FHIR server.

Fix the patient picker pagination

Currently, the presence of "Next" button depends on the value of the "total" field in the response bundle. However, the total field is often missing, which leads to incorrect behavior.

Bug: iss is always https on app launch for Provider EHR Launch

The following line changes the iss to always use https:

var iss = openUrl.replace(/^https?/, "https");

This will cause the app launch to fail when running smart-launcher without SSL (e.g. in dev mode). There is no need to change from http to https since smart-launcher is serving up the page and proxying requests.

NOTE - the logic for changing to https is not present for the "Test With Sample App" path, it just uses the openUrl

I am happy to submit a PR if desired, I am new to this project though so wanted to check first to see if I am missing something before doing so

aud claim missing from id_token

The id_token appears to be missing the aud claim in the response to the token request.

The source appears to set the aud claim from the request body. However, I found it necessary to change it to the client_id that was received in the originating authentication request, which is bounced back through the code JWT.

Does not work with node 12

I am getting compile errors with ursa when running npm install with node 12. I was able to get it to build and run with node 8. If a specific version of node is required, can you please add it to documentation? A quick section with how to build and run would be useful too.

Remove sandboxes

This was an experimental feature. It is never used, not supported, and only complicates the code.

backend service capabilities not reflected in .well-known/smart-configuration

Notably, for backend service auth, the HL7 spec states (http://hl7.org/fhir/smart-app-launch/client-confidential-asymmetric.html#discovery-requirements) that backend services must advertise support for asymmetric keys in .well-known/smart-configuration by:

  1. Including "client-confidential-asymmetric"
  2. token_endpoint_auth_methods_supported that include private_key_jwt
  3. token_endpoint_auth_signing_alg_values_supported include at least one of RS384 and ES384

Retrieving the smart-configuration:

  1. Does not include (1), above
  2. Does not include (2), above
  3. Does not include (3), above.

And, yet, the flow from the DSTU2 example javascript seems to work fine...

config.js:19 throw new Error(`The "${name} environment variable must be set

Hi
How to fix the issue.

Error: The "FHIR_SERVER_R2 environment variable must be set
at D:\smart-appone\smart-launcher\src\config.js:19:11
at Array.forEach ()
at Object. (D:\smart-appone\smart-launcher\src\config.js:17:3)
at Module._compile (node:internal/modules/cjs/loader:1105:14)
at Object.Module._extensions..js (node:internal/modules/cjs/loader:1159:10)
at Module.load (node:internal/modules/cjs/loader:981:32)
at Function.Module._load (node:internal/modules/cjs/loader:822:12)
at Module.require (node:internal/modules/cjs/loader:1005:19)
at require (node:internal/modules/cjs/helpers:102:18)
at Object. (D:\smart-appone\smart-launcher\src\index.js:4:20)
image

Calling https://launch.smarthealthit.org/.well-known/openid-configuration/ gives Cannot GET /auth/authorize

When calling
https://launch.smarthealthit.org/.well-known/openid-configuration/

Gives Cannot GET /auth/authorize

, is it because the payload has extra "/"?

https://launch.smarthealthit.org/.well-known/openid-configuration/

returns

authorization_endpoint: "https://launch.smarthealthit.org/.well-known/openid-configuration//auth/authorize",
token_endpoint: "https://launch.smarthealthit.org/.well-known/openid-configuration//auth/token",
introspection_endpoint: "https://launch.smarthealthit.org/.well-known/openid-configuration//auth/introspect",

The "iss" Value within the OIDC "id_token" does NOT fully support Standard OIDC Validation

Vlad,

I just carried over the discussion we were having at the user-group over to this issue. I'll implement the well-known values as you suggest below.

Any thoughts on allowing the user to specify the client-id (like within an edit control similar to the patient-id and encounter-id) of their application so that when the OIDC validation is performed, the launcher can issue an id_token with their client-id as the "aud" value? It would be merely placing the value specified into the id_token it constructs so that when the SMART app's OIDC validation kicks in, it will truly pass the test.

Dave

<<<<<<<<<<<<<<<<<<<<<<<<<<<<

Hi Dave,

Nice to see that you've found the missing pieces! The authorization_endpoint and token_endpoint should have "well-known" values. You can borrow those from wellKnownSmartConfiguration.js. The client_id should be dynamic and come from the app that is being launched. If you use the built-in sample app, it will have the hardcoded value of "whatever", which is just an attempt to be explicit about the fact that this launcher does not care about this value. Other apps may have other client IDs though.

Finally, how about moving this discussion to GitHub so that we don't bother this global forum with specific coding issues.

Thanks,
Vlad

On Mon, Jul 27, 2020 at 2:02 PM David Vaccaro [email protected] wrote:

Vlad,

Im all up and running with the project and I have a workable solution but I'd like to get your input on a few points.

1. The only OIDC configuration items that appear to be required in order to enable the standard OIDC id-token validation to pass are as follows:

    - issuer: MUST exactly match the value of the iss (issuer) Claim.  
    - jwks_uri: The uri to the encryption key information. (launcher already properly supporting this element)
    - authorization_endpoint: The uri to the OAUTH2 "authorize" endpoint.
    - token_endpoint: The uri to the OAUTH2 "token" endpoint.
    - subject_types_supported: The type of subject identifier used.  The value "public" should be used to indicate that this provides the same sub (subject) value to all Clients.
 
The elements that need to be added are, "issuer", "authorization_endpoint", "token_endpoint" and "subject_types_supported"... I'll read the code the better understand how I can properly formulate the first three, the last one is jut hard-coded.  I'll submit a PR with these additions using the method you outlined above.

2. Epic App Orchard Launcher adds the following STATIC items to their OIDC discovery metadata but I've determined that these may be generally optional:

    - id_token_signing_alg_values_supported: The current type of signing algorithm used for the token (same as in header) defaults to "RS256"
     - response_types_supported: The type of the response... I believe this is supposed to be "id_token" but Epic shows "code" (this might be wrong on their part).
     - scopes_supported: The scopes supported during token negotiations.  Epic has: "fhirUser", "launch" and "openid". 
     - grant_types_supported: The grant types supported during token negotiations.  Epic has: "authorization_code", "refresh_token", "client_credentials" and "urn:ietf:params:oauth:grant-type:jwt-bearer".
     - token_endpoint_auth_methods_supported: the token endpoint methods supported during the token negotiations.  Epic has: "client_secret_post", "client_secret_basic" and "private_key_jwt".
     - response_modes_supported: The response modes supported.  Epic has: "query"

I'll document these within the PR just in case that these should need to be added in a later PR.

3. The value of the "aud" within the id_token is defined to be as follows:

Audience(s) that this ID Token is intended for. It MUST contain the OAuth 2.0 client_id of the Relying Party as an audience value. It MAY also contain identifiers for other audiences. In the general case, the aud value is an array of case sensitive strings. In the common special case when there is one audience, the aud value MAY be a single case sensitive string.

Currently, the id_token coming back from the SMART launcher always has the value "whatever" as the value of the "aud".  I think to properly support this field, we would need to introduce a "client-id" entry field in the tool that allows the user to specify a "client-id" to use during the launch process.  This value could be pre-populated with a random GUID value and, more or less, collapsed in some section so that it doesn't overwhelm the UI.  The user that needs to set this value could hunt it out, expand it and set it if needed..... any thoughts?

Dave


On Friday, July 24, 2020 at 7:53:46 PM UTC-4, Vladimir Ignatov wrote:

    Hi Dave,

    It seems to me that this functionality would depend on what the OidcIdTokenValidator expects to find in the metadata. 
    The project is open source and I am supposed to maintain it. However, I am currently struggling with few deadlines and may not be able to address this for a while. It would be very helpful If you can make a PR or just open an issue to document what you find out. Here are some hints to help you get started:

    1. The ".well-known/openid-configuration" is currently very simple and is defined inline at https://github.com/smart-on-fhir/smart-launcher/blob/master/src/index.js#L86-L89.
    2. if it has to be augmented, it will be better implement it in a separate file similar to the ".well-known/smart-configuration" -  https://github.com/smart-on-fhir/smart-launcher/blob/master/src/index.js#L86-L89 and https://github.com/smart-on-fhir/smart-launcher/blob/master/src/wellKnownSmartConfiguration.js
    3. The best way to work on it locally is to use Node 14.2.0 and VScode. Then "git clone https://github.com/smart-on-fhir/smart-launcher.git", "cd smart-launcher", "npm i". Then in VScode add it as a project folder and in the debug panel run the "Launch" task.

    Thanks,
    Vlad

    On Fri, Jul 24, 2020 at 1:17 PM David Vaccaro <[email protected]> wrote:

        Vlad,

        I'm not exactly sure as of yet what in particular is missing that prevents standard OIDC token validation but my intention was to leverage Spring OATH2 client library to perform the validation as follows:

                        // NOTE the "header" is the id-token header values, the "payload" is the id-token payload values

        		// Establish the client-registration
        		ClientRegistration registration = ClientRegistrations
        				.fromOidcIssuerLocation(payload.get("iss"))
        				.clientId("my client id")
        				.clientSecret("my client secret").build();

        		// Create the OIDC token validator
        		OidcIdTokenValidator validator = new OidcIdTokenValidator(registration);

        		// Validate the id-token
        		OAuth2TokenValidatorResult result = validator.validate(new Jwt(id_token,
        	    		Instant.ofEpochSecond((Integer)payload.get("iat")),
        	    		Instant.ofEpochSecond((Integer)payload.get("exp")),
        	    		header,
        	    		payload));

        NOTE: That this validation works when launched within the Epic SMART on FHIR Launch Simulator.

        I realize that the SMART Health IT Launcher is primarily intended to support the basic flow through to the SMART launch process for basic testing purposes but, I find this launcher to be VERY useful when developing my SMART apps.  

        None of the other simulators or launchers that I have used is as feature rich (supporting different versions of FHIR, all launch flows, CDS Hooks, standard error cases, etc.) so I would highly encourage maintainers to make the launcher issue real tokens and fully support OIDC token validation (as well as any other aspect of the SMART/OAUTH2 standard relevant to SMART).  

        In fact, if this is an open-source initiative, I would be happy to implement this myself if that is possible.

        In the meantime, I'll see if I can determine what might be missing from the config that is failing the above code.

        Dave 


        On Friday, July 24, 2020 at 12:16:17 PM UTC-4, Vladimir Ignatov wrote:

            It should be noted that the launcher is not a real auth server, but a tool design to make testing a SMART app easier for developers. It often just simulates the real-world setup. For example, no client registration is needed and any client_is accepted as if it has been registered already.

            With that said, supporting only the minimal metadata made sense when that was being implemented. We could add more, although I am not sure if that would be enough for token validation. Which "well-known" properties are you missing?

            Thanks,
            Vlad



            On Thu, Jul 23, 2020 at 3:03 PM David Vaccaro <[email protected]> wrote:

                All,

                Looks like the endpoint identified by the "iss" value within the OpenID Connect "id token" issued by the launcher does NOT fully support enough Open ID Connect metadata in order to validate the "id token".

                Here is the "iss" served by an instance of a launch within the launcher WITH the additional standard metadata path appended:

                https://launch.smarthealthit.org/.well-known/openid-configuration

                Notice that it serves the absolute minimum metadata which is NOT enough to properly enforce the OIDC validation rule (specified within the SMART spec).

                For example, Google hosts the following for a similar OIDC endpoint:

                https://accounts.google.com/.well-known/openid-configuration

                Anyone encounter this?  I'm just trying to develop/test (using the launcher) the complete validation of the id-token to prove my app strictly adheres to the standard.

                Thanks,
                Dave

                -- 
                You received this message because you are subscribed to the Google Groups "SMART on FHIR" group.
                To unsubscribe from this group and stop receiving emails from it, send an email to [email protected].
                To view this discussion on the web visit https://groups.google.com/d/msgid/smart-on-fhir/76a9e6bc-37e4-4bcf-b47b-2bccb46d39e8o%40googlegroups.com.

        -- 
        You received this message because you are subscribed to the Google Groups "SMART on FHIR" group.
        To unsubscribe from this group and stop receiving emails from it, send an email to [email protected].
        To view this discussion on the web visit https://groups.google.com/d/msgid/smart-on-fhir/f3973a70-ba1e-4948-88b2-aa5e2527612bo%40googlegroups.com.

-- 
You received this message because you are subscribed to the Google Groups "SMART on FHIR" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [email protected].
To view this discussion on the web visit https://groups.google.com/d/msgid/smart-on-fhir/e9f475d9-9792-4cbf-94a1-a52f74a00528o%40googlegroups.com.

--
You received this message because you are subscribed to a topic in the Google Groups "SMART on FHIR" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/smart-on-fhir/Ibe8F0op2hY/unsubscribe.
To unsubscribe from this group and all its topics, send an email to [email protected].
To view this discussion on the web visit https://groups.google.com/d/msgid/smart-on-fhir/CACJzUGccNyDYtw_%3Di4NnE6NyY5uO8is98hdixrJkdir58PHCqA%40mail.gmail.com.

Backend Services token not verified properly

Currently it uses jwt.verify incorrectly:

jwt.verify(req.body.client_assertion, base64url.decode(clientDetailsToken.pub_key), { algorithm: "RS256" });

Instead, we should provide an algorithms array containing all the RS and ES algorithms.

DiagnosticReport posted for newly posted Patient is not found by standard search methods using information from Patient

I'm trying to find DiagnosticReport resources for a given patient using the SmartHealthIt R4 server for testing (https://r4.smarthealthit.org/).

I've loaded a new Patient and new DiagnosticReport using the id of the patient, for example:

A Patient resource that contains this:
"resourceType": "Patient",
"id": "1435573",

And a DiagnosticReport that has this:

"subject": {
"reference": "Patient/1435573"
}

None of these searches returns the DiagnosticReport. The queries below are based on Hapi FHIR: searching for patient identifier using Postman. All of the queries give basically an empty bundle (contains only the self url) except for the last that gives an invalid query error.

Queries:

https://r4.smarthealthit.org/DiagnosticReport?subject=1435573

https://r4.smarthealthit.org/DiagnosticReport?patient.identifier=1435573

https://r4.smarthealthit.org/DiagnosticReport?subject=Patient/1435573

https://r4.smarthealthit.org/DiagnosticReport?subject=https://r4.smarthealthit.org/Patient/1435573

https://r4.smarthealthit.org/DiagnosticReport?subject=http://r4.smarthealthit.org/Patient/1435573

https://r4.smarthealthit.org/DiagnosticReport?subject.reference=1435573 (invalid parameter chain error)

More details are available here:
https://stackoverflow.com/questions/74510589/fhir-not-finding-diagnosticreport-for-patient-id-on-smarthealthit-server

Binary Resources

via SMART discussion group:

Sending Binary with pdf, png, or jpg - Posted content Base64 is different and incorrect when read back from Launch FHIR Server.

When I use the same code hitting the Hapi FHIR public Test server, it works correctly.

Is Binary not supposed to support POST/PUT on that server?

Support CDS Hooks appContext for SMART app launches

CDS Hooks defines the notion of an 'appContext' field which allows a CDS Service to share data between a SMART app link in a CDS card and their SMART app. Effectively, the SMART app link in a CDS card contains appContext and the EHR passes this value back alongside the access token when the SMART app launches.

See this thread on the CDS Hooks mailing list for additional history/context.

See also https://cds-hooks.org/specification/1.0/#link

An optional field that allows the CDS Service to share information from the CDS card with a subsequently launched SMART app. The appContext field should only be valued if the link type is smart and is not valid for absolute links. The appContext field and value will be sent to the SMART app as part of the OAuth 2.0 access token response, alongside the other SMART launch parameters when the SMART app is launched. Note that appContext could be escaped JSON, base64 encoded XML, or even a simple string, so long as the SMART app can recognize it.

redirectUri does not allow query params

We are testing out a new service and found that if we include query params in the redirectUri it breaks the launcher as it no longer appends the state and code parameters.

I can't see this in the spec so unsure whether this is intentional or a bug?

MedicationStatement endpoint never returns any Medication resources

When launching a SMART app from the launcher, trying to GET the /MedicationStatement endpoint never appears to return any actual Medication resources, even for patients who have medications listed. According to the documentation here, I would expect to get a collection of Medication resources for a patient if I hit that endpoint. No matter what patient I try or how many medications they have listed, this is always returned:

{
  "resourceType": "Bundle",
  "id": "5ef12489-213f-4afd-82d8-514507f546d4",
  "meta": {
    "lastUpdated": "2018-11-19T09:06:54.594-05:00"
  },
  "type": "searchset",
  "total": 0,
  "link": [
    {
      "relation": "self",
      "url": "https://launch.smarthealthit.org/v/r3/fhir/MedicationStatement?_format=application%2Fjson&patient=smart-1685497"
    }
  ]
}

Is GET /MedicationStatement not properly implemented or am I maybe doing something wrong?

launched iframe (Simulate launch within the EHR user interface) session cookie different during initiation and launch

Hi,

I am using the SMART App Launcher. When I launch with "Simulate launch within the EHR user interface" checked it seems that my cookies do not persist between the the first request to my server and the callback (ie my my server is creating a new session for the callback). When I launch without this option checked, everything works as fine.

My app launch url is pointing to my Rails server running on https with puma and a self signed cert. Session management is done by Rails.

"Validate that my client performs PKCE" not doing anything if client does not use PKCE

Auth Server at https://launch.smarthealthit.org/v/r4/fhir does not validate existence of redirect_uri

When sending an authorization request that includes a 'redirect_uri' the /token endpoint should fail the code exchange request if the same redirect_uri was not included.

The last paragraph in this section explains it:
https://tools.ietf.org/html/rfc6749#section-4.1.3

Even though this is a test server, if developers use this to verify their implementation their code potentially fails in a real system.

"tests" vs "test"

The repo has a top-level tests directory and a test directory. I think only test is being used (e.g., by scripts defined in package.json). Can "tests" be deleted?

Fix needed for cross-frame messaging

In the patient picker, when the user clicks on a patient to select it the script attempts to call a function in the parent frame - https://github.com/smart-on-fhir/smart-launcher/blob/master/static/picker.js#L48. This works because in some cases the picker is rendered in a frame and if it isn't, the entire call is in a try/catch block. However, This is still generating console errors during the app launch flow and many people are concerned about that.

The goal is to replace that with a postMessage based solution that shouldn't generate any errors. Note that there might be similar issues with the encounter picker at https://github.com/smart-on-fhir/smart-launcher/blob/master/static/encounter-picker.html#L241 the user picker at https://github.com/smart-on-fhir/smart-launcher/blob/master/static/login.html#L156

.wellknown/smart-configuration does not include url segments in auth endpoints

The authorization_endpoint and token_endpoint in the .wellknown/smart-configuration file do not include the optional FHIR server URL segments. For example, the json at http://launch.smarthealthit.org/v/r3/sim/eyJrIjoiMSIsImoiOiIxIiwiYiI6IjdiNjk3MzIyLTM2MDctNDZjYi1hMjQwLWMwODFiY2NiYTJlNSJ9/fhir/.well-known/smart-configuration has an authorization_endpoint of http://launch.smarthealthit.org/v/r3/auth/authorize. Since the authorize endpoint expects its base URL to match that of the FHIR endpoint, the aud check will fail. This is handled correctly in the authorization extensions the launcher adds to the capability statement which are dynamically generated to reflect FHIR server's actual url path.

invalid us-core-race and us-core-ethnicity extensions in STU3 synthetic Patient data

The us-core-race and us-core-ethnicity extensions both use a nested extension structure:

https://hl7.org/implement/standards/fhir/us/core/STU2/StructureDefinition-us-core-race.html
http://hl7.org/implement/standards/fhir/us/core/STU2/StructureDefinition-us-core-ethnicity.html

However, some of the https://r3.smarthealthit.org synthetic Patient data uses (incorrectly) valueCodeableConcept for those extensions.

For example, in Patient https://r3.smarthealthit.org/Patient/f0462936-eb4b-4da1-b45a-fbd96ebf8ccb the (incorrect) us-core-race and us-core-ethnicity extensions look like this (valueCodeableConcept):

  "extension": [
    {
      "url": "http://hl7.org/fhir/us/core/StructureDefinition/us-core-race",
      "valueCodeableConcept": {
        "coding": [
          {
            "system": "http://hl7.org/fhir/v3/Race",
            "code": "2106-3",
            "display": "White"
          }
        ],
        "text": "race"
      }
    },
    {
      "url": "http://hl7.org/fhir/us/core/StructureDefinition/us-core-ethnicity",
      "valueCodeableConcept": {
        "coding": [
          {
            "system": "http://hl7.org/fhir/v3/Ethnicity",
            "code": "2186-5",
            "display": "Nonhispanic"
          }
        ],
        "text": "ethnicity"
      }
    },
...

... but they should look more like this (nested extensions):

  "extension": [
    {
      "url": "http://hl7.org/fhir/us/core/StructureDefinition/us-core-race",
      "extension": [
        {
          "url": "ombCategory",
          "valueCoding": {
            "system": "urn:oid:2.16.840.1.113883.6.238",
            "code": "2106-3",
            "display": "White"
          }
        },
        {
          "url": "text",
          "valueString": "white"
        }
      ]
    },
    {
      "url": "http://hl7.org/fhir/us/core/StructureDefinition/us-core-ethnicity",
      "extension": [
        {
          "url": "ombCategory",
          "valueCoding": {
            "system": "urn:oid:2.16.840.1.113883.6.238",
            "code": "2186-5",
            "display": "Non Hispanic or Latino"
          }
        },
        {
          "url": "text",
          "valueString": "non-Hispanic"
        }
      ]
    },
...

Several examples of synthetic Patient data with this problem are:

https://r3.smarthealthit.org/Patient/f0462936-eb4b-4da1-b45a-fbd96ebf8ccb
https://r3.smarthealthit.org/Patient/008076b8-7fba-4645-ba41-0b8ffe10e82b
https://r3.smarthealthit.org/Patient/eb3271e1-ae1b-4644-9332-41e32c829486

If possible, please revise the synthetic Patient data to correctly represent the us-core-race and us-core-ethnicity extensions.

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.