Giter Club home page Giter Club logo

openid4vc's People

Contributors

nanderstabel avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

openid4vc's Issues

Hard split of the different OID4VC crates

Description

Specifically the SIOPv2, OID4VC and OID4VCI crates need to be properly split. All common/shared code should be migrated to a new crate: oid4vc-core or oid4vc-common.

Whenever SIOPv2 or OID4VC should be used in unison, their respective feature flags should be used.

Motivation

This is necessary in order for SIOPv2 to be used with and without OID4VP and vice versa.

Resources

No response

To-do List

  • Create a new crate and migrate all shared code there.
  • Implement feature flags so that SIOPv2 and OID4VP can be used in unison
  • Add an integration test to test the implicit flow for siopv2+oid4vp

Support DIF Presentation Exchange Features

Description

DIF Presentation Exchange describes multiple features that can be added to the basic presentation exchange process: https://identity.foundation/presentation-exchange/spec/v2.0.0/#features

#21 needs to be merged first.

#33 and #38 have priority.

Motivation

Adding support for all or some of them will increase the interoperability of this library.

Resources

https://identity.foundation/presentation-exchange/spec/v2.0.0/#features

To-do List

Add support for multiple Subject Syntax Types

Description

There is a multitude of different DID Methods, however, not all Relying Parties and Providers will necessary will have all of them implemented. This library will provide the Subject trait that can be used to implement custom DID Methods. Both the RelyingParty, as well as the Provider should be able to ingest multiple Subject Syntax Types.

SIOPv2 defines two Subject Syntax Types:

  • JWK Thumbprint subject syntax type: When this type is used, the sub claim value must be the base64url encoded representation of the JWK thumbprint of the key in the sub_jwk claim, and sub_jwk must be included in the Self-Issued Response.

  • Decentralized Identifier (DID) subject syntax type: When this type is used, the sub value must be a DID as defined in [DID-Core], and sub_jwk must not be included in the Self-Issued Response. The subject syntax type must be cryptographically verified against the resolved DID Document as defined in the SIOPv2 ID Token validation section.

Motivation

The user of this library should be able to select their preferred subject syntax type both as a Relying Party as well as for a Provider.

By adding support for Subject Syntax Types, this SIOP library will be able to generate and validate ID Tokens that conform to the latest version of the SIOP specification. This will enhance the interoperability of our SIOP implementation with other SIOP-compliant systems and improve the security and reliability of the authentication process.

Resources

https://openid.net/specs/openid-connect-self-issued-v2-1_0.html#name-subject-syntax-types

To-do List

  • Implement option to add multiple SubjectSyntaxType's to the Provider.
  • Implement option to add multiple SubjectSyntaxType's to the Relying Party.

Fork the "Sphereon demo website" GitHub repository and build a test framework

Description

We will fork the Sphereon demo website's GitHub repository and build a test framework. This step will involve setting up a local development environment, running automated tests, and debugging any issues that arise. Some of the default settings in the dotenv files of both the frontend and backend need to be tweaked by adding a did:key did and its private key.

A script should be provided that will spin up the demo website when tests are performed.

Motivation

In order to test the validity of the library, we need to test the interoperability with other SIOP implementations.

Resources

Sphereon demo website

To-do List

  • Fork and adjust the demo website repository
  • Provide a script for running the server

Add VP Token validation

Support request_uri and validation

Description

Fork the Sphereon demo website and alter some variables so that it suits our needs (alter the default DID method to did:key, add test private keys, etc) and so that it can be added as an integration test.

The demo website follows the following steps:

  • A redirect_uri is presented in the form of a QR Code (by the relying party).
  • The Provider needs to follow this uri in order to receive the JWT encoded SIOP Response.
  • The Provider needs to decode and validate this JWT.
  • The Provider generates a SIOP Response which includes a (JWT encoded) id_token and sends it to the Relying Party's redirect_uri

In order for this integration to work some Request parameters need to be added as well, such as the request_uri and the registration parameter.

Motivation

In order to test the validity of the library, we need to test the interoperability with other SIOP implementations.

Resources

Depends on #2
Sphereon demo website

To-do List

Support OID4VCI Pre-Authorized Code Flow

Description

+--------------+   +-----------+                                    +-------------------+
| User         |   |   Wallet  |                                    | Credential Issuer |
+--------------+   +-----------+                                    +-------------------+
        |                |                                                    |
        |                |  (1) User provides  information required           |
        |                |      for the issuance of a certain Credential      |
        |-------------------------------------------------------------------->|
        |                |                                                    |
        |                |  (2) Credential Offer (Pre-Authorized Code)        |
        |                |<---------------------------------------------------|
        |                |  (3) Obtains Issuer's Credential Issuer metadata   |
        |                |<-------------------------------------------------->|
        |   interacts    |                                                    |
        |--------------->|                                                    |
        |                |                                                    |
        |                |  (4) Token Request (Pre-Authorized Code, pin)      |
        |                |--------------------------------------------------->|
        |                |      Token Response (access_token)                 |
        |                |<---------------------------------------------------|
        |                |                                                    |
        |                |  (5) Credential Request (access_token, proof(s))   |
        |                |--------------------------------------------------->|
        |                |      Credential Response                           |
        |                |      (credential(s))                               |
        |                |<---------------------------------------------------|

Figure 2: Issuance using Pre-Authorized Code Flow

  1. The Credential Issuer successfully obtains consent and user data required for the issuance of a requested Credential from the End-User using Issuer specific business process.
  2. The flow defined in this specification begins as the Credential Issuer generates a Credential Offer for certain Credential(s) and communicates it to the Wallet, for example as a QR code or as a deeplink.
  3. The Wallet uses information from the Credential Offer to obtain the Credential Issuer's metadata including details about the Credential that this Credential Issuer wants to issue. This step is defined in Section 10.2.
  4. The Wallet sends the Pre-Authorized Code obtained in step (2) in the Token Request to the Token Endpoint. The Wallet will send a PIN provided by the User, if it was required by the Credential Issuer. This step is defined in Section 6.
  5. This step is the same as Step 5 in the Authorization Code Flow.

Motivation

No response

Resources

https://openid.bitbucket.io/connect/openid-4-verifiable-credential-issuance-1_0.html#name-pre-authorized-code-flow

To-do List

  • Support OID4VCI Pre-Authorized Code Flow

Refactor RequestUrl so that it can handle optional protocol and domain name

Description

Refactor RequestUrl so that it can handle a requests' protocol, and (sub)domain dynamically, as mentioned here: #17 (comment)

Motivation

At the moment request urls are statically prefixed with siopv2://idtoken. This protocol and (sub)domain part of the url should be dynamic though, and therefore the RequestUrl enum should be refactored, possible by converting it to a struct instead.

Resources

No response

To-do List

No response

Support OID4VCI Authorization Code Flow

Description

+--------------+   +-----------+                                    +-------------------+
| User         |   |   Wallet  |                                    | Credential Issuer |
+--------------+   +-----------+                                    +-------------------+
        |                |                                                    |
        |    interacts   |                                                    |
        |--------------->|                                                    |
        |                |  (1) Obtains Issuer's Credential Issuer metadata   |
        |                |<-------------------------------------------------->|
        |                |                                                    |
        |                |  (2) Authorization Request                         |
        |                |      (type(s) of Credentials to be issued)         |
        |                |--------------------------------------------------->|
        |                |                                                    |
        |   User Authentication / Consent                                     |
        |                |                                                    |
        |                |  (3)   Authorization Response (code)               |
        |                |<---------------------------------------------------|
        |                |                                                    |
        |                |  (4) Token Request (code)                          |
        |                |--------------------------------------------------->|
        |                |      Token Response (access_token)                 |
        |                |<---------------------------------------------------|
        |                |                                                    |
        |                |  (5) Credential Request (access_token, proof(s))   |
        |                |--------------------------------------------------->|
        |                |      Credential Response                           |
        |                |      (credential(s) OR transaction_id)           |
        |                |<---------------------------------------------------|

Figure 1: Issuance using Authorization Code Flow

  1. The Wallet uses the Credential Issuer's metadata Section 10.2 to learn what credential types and formats the Credential Issuer supports and to determine the issuer URL of the OAuth 2.0 Authorization Server the Credential Issuer relies on. Note in this example, the Credential Issuer also provides the OAuth 2.0 Authorization Server. This specification enables deployments where the Credential Issuer API and the Authorization Server are different services, perhaps even provided by different entities.
  2. The Wallet sends an Authorization Request to the Authorization Endpoint. The Authorization Endpoint processes the Authorization Request, which typically includes user authentication and gathering of user consent.
  3. The Authorization Endpoint returns an Authorization Response with the Authorization Code upon successfully processing the Authorization Request.Note: steps (2) and (3) happen in the frontchannel, by redirecting the End-User via the User Agent. Those steps are defined in Section 5.
  4. The Wallet sends a Token Request to the Token Endpoint with the Authorization Code obtained in step (3). The Token Endpoint returns an Access Token in the Token Response upon successfully validating Authorization Code. This step happens in the backchannel using server to server communication. This step is defined in Section 6.
  5. The Wallet sends a Credential Request to the Credential Issuer's Credential Endpoint with the Access Token and (optionally) the proof of possession of the public key to which the the issued VC shall be bound to. Upon successfully validating Access Token and proof, the Credential Issuer returns a VC in the Credential Response if it is able to issue a Credential right away. This step is defined in Section 7.

Motivation

No response

Resources

https://openid.bitbucket.io/connect/openid-4-verifiable-credential-issuance-1_0.html#name-authorization-code-flow

To-do List

  • Support OID4VCI Authorization Code Flow

Add support for the did:key method

Description

Add a new DidMethod variant for the did:key method. It should follow a similar implementation to the did:iota method (by implementing the Subject trait.

Motivation

The did:iota is not (yet) widely accepted by relying parties. Therefore, we should add support for the did:key method is supported by the DIF Universal Resolver and is therefore supported by this SIOP library. There is already a rust implementation for did:key

When the did:key method is properly implemented, this will add validity to our own SIOP implementation.

Resources

DIF Universal Resolver
https://github.com/RadicalLedger/did-siop-lib
did:key in rust

To-do List

  • Add new DidMethod Key which implements the Subject trait

Implement JWT decoding and validation for the Provider struct

Description

We will implement JWT decoding and validation for the Provider struct. This will involve decoding the JWT and verifying its signature to ensure its authenticity. Additionally, we will perform other validation checks such as checking the issuer and audience claims.

Motivation

When a SiopRequest is particularly large (for example when it includes a Presentation Definition for Verifiable Presentations, or many claims) a Relying Party may choose to send its request by value by utilizing the request_uri. The actual SiopRequest that can be retrieved by following this redirect uri may or may not be encoded as a JWT. Therefore, the Provider should be capable of decoding and validation JWT's

Resources

SIOPv2 section 9.

To-do List

  • Support JWT validation for the Provider struct.

Make IdToken feature complete

Description

Make the IdToken struct fully feature complete as mentioned here: #17 (comment)

Also, implement a builder pattern either specifically for IdToken and/or for a Token enum with (at least) the IdToken(IdToken) and VpToken(VpToken) variants. They should directly map to the already existing ResponseType enum that's being used in the SiopRequest struct.

Motivation

The IdToken should be fully feature complete in order to be 100% conform to the SIOPv2 specification.

The IdToken can have several different kind of claims, such as the openid standard claims, jwt registered claims, and other claims. The following specifications need to be followed:

Resources

To-do List

No response

Add initial support for verifiable presentations

Description

Add initial support for verifiable presentations.

This Issue involves adding support for the vp_token response_type and presentation_definition parameter in the SIOP library, as described in the OpenID for Verifiable Presentations (OpenID4VP) specification. When the vp_token response type is set, the Provider should generate a presentation of verifiable credentials based on the presentation_definition parameter in the request. The Relying Party should then be able to verify the presentation and authenticate the user based on the claims presented.

This implementation should initially be following the IOTA DID method.

Motivation

Apart from the SIOPv2 specification, this library should also be able to support the OpenID for Verifiable Presentations specification. The initial implementation as described above will also allow for testing this library against external Client libraries.

Resources

OpenID for Verifiable Presentations (OpenID4VP) specification
#4

To-do List

  • Add a PresentationDefinition struct that can be deserialized from a SiopRequest
  • Add a VerifiablePresentation struct that can be build from a PresentationDefinition
  • Provide a presentation_submission parameter alongside the new vp_token in the SiopResponse
  • Verifiable Presentations functionality should be behind a openid4vp feature flag

Support the registration parameter

Description

We will add support for the registration parameter, which allows clients to provide some metadata about themselves to the Provider. This will help to simplify the process of requesting access to protected resources. More details: SIOPv2 section 7.2.2.

Motivation

Members of the registration parameter are needed for the Provider to know if it is compatible with the Relying Party. Example of a registration parameter in a decoded JWT:

"registration": {
    "id_token_signing_alg_values_supported": [
      "EdDSA",
      "ES256",
      "ES256K"
    ],
    "request_object_signing_alg_values_supported": [
      "EdDSA",
      "ES256",
      "ES256K"
    ],
    "response_types_supported": [
      "id_token"
    ],
    "scopes_supported": [
      "openid did_authn"
    ],
    "subject_types_supported": [
      "pairwise"
    ],
    "subject_syntax_types_supported": [
      "did",
      "did:ethr",
      "did:key",
      "did:ion",
      "did:jwk"
    ],
    "vp_formats": {
      "jwt_vc": {
        "alg": [
          "EdDSA",
          "ES256K"
        ]
      },
      "jwt_vp": {
        "alg": [
          "ES256K",
          "EdDSA"
        ]
      }
    }

Resources

SIOPv2 section 7.2.2

To-do List

  • Support for the registration parameter and its members.

Add Authorization Code Flow support

Description

We need to add support for the Authorization Code Flow as described in SIOPv2 and in section 3.1.2 of the OIDC-core standard. The Authorization Code Flow is summarized as follows:

  • The Relying Party creates a SIOP Request with a response_type parameter of "code".
  • The Provider receives the SIOP Request and validates it.
  • The Provider presents an authorization prompt to the user and obtains their consent.
  • The Provider generates an Authorization Code and associates it with the user and the Relying Party.
  • The Provider redirects the user's browser to the Relying Party's redirect_uri with the Authorization Code.
  • The Relying Party receives the Authorization Code and sends a direct HTTP POST request to the Provider to exchange it for an ID Token and an Access Token.
  • The Provider validates the Authorization Code and generates an ID Token and an Access Token.
  • The Provider sends the ID Token and the Access Token back to the Relying Party in the HTTP response.

We should be able to add support for the Authorization Code Flow in the SIOP Requests, and the Provider should be able to handle the Authorization Code and generate the ID Token and Access Token accordingly. The library should also be able to perform the necessary token exchange requests to obtain the tokens from the Provider.

Motivation

The Authorization Code Flow is an important OAuth 2.0 flow that provides a more secure way of obtaining access tokens compared to the Implicit Flow. This flow is widely used in many applications and is also supported by major identity providers, such as Google and Microsoft. By adding support for the Authorization Code Flow in SIOPv2, we can provide users and developers with a more secure and standardized way of obtaining access tokens, which is crucial for the protection of sensitive user data. Additionally, this will enable more seamless integration of SIOPv2 with other OAuth 2.0 based systems, providing greater flexibility in application design and implementation.

Resources

SIOPv2
section 3.1.2 of the OIDC-core standard

To-do List

  • Add support for generating an Authorization Code for Provider
  • Implement a token_endpoint for the Provider

Enhance documentation

Description

The documentation of the individual crates should be easy to understand for first-time users. Simple diagrams on how the individual crates are related should be included.

Motivation

Improve developer experience

Resources

No response

To-do List

  • add README.md to each crate in the monorepo
  • generate and publish docs to a website (docs.impierce.com?)
  • add diagrams

Rename repo to oid4vc

Description

Rename repo to oid4vc.

Make sure all depending crates follow the name change.

Motivation

Rename repo to oid4vc in order to match the crates name.

Resources

No response

To-do List

  • Change repo name

Implement same-device flow redirection including Error Response

Description

The error response must be made in the same manner as defined in Section 3.1.2.6 of [OpenID.Core].In addition to the error codes defined in Section 4.1.2.1 of OAuth 2.0 and Section 3.1.2.6 of [OpenID.Core], this specification also defines the following error codes:

  • user_cancelled: End-User cancelled the Authorization Request from the RP.

  • registration_value_not_supported: the Self-Issued OP does not support some Relying Party parameter values received in the request.

  • subject_syntax_types_not_supported: the Self-Issued OP does not support any of the Subject Syntax Types supported by the RP, which were communicated in the request in the subject_syntax_types_supported parameter.

  • invalid_registration_uri: the client_metadata_uri in the Self-Issued OpenID Provider request returns an error or contains invalid data.

  • invalid_registration_object: the client_metadata parameter contains an invalid RP parameter Object.
    Other error codes MAY be used.Note that HTTP error codes do not work in the cross-device Self-Issued OP protocol flows.

Motivation

Essential part of the same-device flow.

Resources

https://openid.net/specs/openid-connect-self-issued-v2-1_0.html#section-10.3
OpenID.Core]

To-do List

  • Implement proper redirection for same-device flow
  • Add the Error Responses

Add proper serde and a Builder for RequestUrl

Description

Add a Builder for RequestUrl, as well as serialization and deserialization to and from a request literal string.

Motivation

This will improve the construction of new SiopRequests.

Resources

[Self-Issued OpenID Provider Authorization Request](https://openid.net/specs/openid-connect-self-issued-v2-1_0.html#name-self-issued-openid-provider-a)

To-do List

  • Add a Builder for RequestUrl #17
  • Add serialization and deserialization for RequestUrl #18

Add support for `claims` request parameter

Description

The library should be able to add the request for claims in the SIOP Requests and the Provider should be able to handle those claims accordingly. As specified in the OICD-core standard, claims can be requested by the Relying Party using the claims request parameter.

There are two types of claims:

  • 'self-attested' claims, as specified in the SIOPv2 standard
  • cryptographically verifiable claims issued by trusted third-party sources as described in OpenID4VP

Both of them need to be implemented.

Motivation

In OpenID Connect, a claim is a piece of information about the user, such as their name or email address, that the Relying Party can request from the OpenID Provider. The claims request parameter is used by the Relying Party to specify which claims it wants to receive in the ID token or UserInfo response.

The claims parameter value is a JSON object that describes the set of requested claims, including the name of the claim and any additional parameters needed to process the claim. The OpenID Provider may or may not provide the requested claims based on the user's consent and the authorization policies of the Provider.

By adding support for the claims request parameter, our library will allow Relying Parties to request specific claims about the user and receive them in a standardized way, making it easier for them to integrate with our library and comply with OpenID Connect standards.

Resources

OICD-core claims specification
SIOP-v2
OpenID4VP

To-do List

  • #18
  • #18
  • Support aggregated and distributed claims
  • #9

Implement the request_uri parameter

Description

We will implement support for the request_uri parameter, which allows clients to send a URI reference to a SiopRequest represented as a signed JWT token.

Motivation

When a SiopRequest is particularly large (for example when it includes a Presentation Definition for Verifiable Presentations, or many claims) a Relying Party may choose to send its request by value by utilizing the request_uri. The Provider may follow this URI in order to retrieve the actual SiopRequest as a signed JWT.

Resources

SIOPv2 section 9.

To-do List

  • Support the request_uri parameter in the Provider struct when generating a Response

Retention Feature

Description

The Retention Feature extends the Input Descriptor Object's field object, allowing a Verifier to indicate it will retain the submitted value for the specific field. It is currently presented to support mDL systems, and may be deprecated in the future or moved to a separate specification for more robust mDL interoperability at a later time.

Motivation

See #32

Resources

#32
https://identity.foundation/presentation-exchange/spec/v2.0.0/#retention-feature

To-do List

  • Retention Feature

Add support for QR-Code

Description

In the SIOPv2 cross-device scenario, the Relying Party converts its SIOP Request into a QR Code which presented on one device so that it can be scanned using a second device. The Provider should be able to read that QR Code and construct it back into the original request again.

Motivation

Adding support for QR scanning in the cross-device scenario described in SIOPv2 can greatly improve the user experience and simplify the authentication process. Instead of manually entering authentication codes, users can simply scan a QR code displayed on one device with another device's camera to authenticate. This is especially beneficial in scenarios where users need to authenticate across different devices, such as when logging into a website on a desktop computer with a mobile phone.

QR code scanning is already a familiar and widely used technology for mobile devices, and it can greatly simplify the authentication process by eliminating the need for manual input of complex codes or URLs. By adding support for QR code scanning, the SIOP library can provide a more user-friendly authentication experience, which can increase user adoption and reduce the risk of authentication errors. Additionally, QR code scanning can improve security by providing a more convenient and secure way to authenticate, reducing the likelihood of password sharing or other security risks.

Resources

Cross-Device Self-Issued OpenID Provider Request

To-do List

  • Allow for conversion of a SIOP Request into a QR Code by the RelyingParty
  • Allow for re-constructing a QR Code into a SIOP Request by the Provider

Add Implicit Flow support

Description

We need support for the Implicit Flow for SIOPv2 as described in SIOPv2 as well as in section 7 of the OIDC-core standard. The Implicit Flow is summarized as follows:

  1. The Relying Party creates a SIOP Request specified here.
  2. The Provider receives a SIOP Request.
  3. The Provider validates the request.
  4. The Provider creates a SIOP Response by creating and signing an id_token following this specification and also this specification for ID Tokens.
  5. The Provider sends the SIOP Request to the Relying Party's redirect_uri by a direct HTTP POST request.
  6. The Relying Party receives the SIOP Response and validates it following this specification.

Motivation

The Implicit Flow is a widely used flow in OpenID Connect, and its support is necessary for our SIOP implementation to be fully compliant with the specification. By implementing the Implicit Flow, we can ensure that our SIOP Provider is able to generate and sign ID Tokens that can be securely transmitted to Relying Parties. This will also expand the interoperability of our SIOP Provider with other OpenID Connect compliant systems.

Resources

OIDC-core
SIOPv2

To-do List

  • Add a Provider struct that can validate SIOP Requests and create and sign SIOP Responses (including ID Token).
  • Add a RelyingParty struct that can create and present a SIOP Request as well as validate SIOP Responses.
  • Add support for the IOTA DID method.

Add support for the JWK Thumbprint subject syntax type

Description

The JWK Thumbprint subject syntax type is a type of Subject Syntax Type used in the Self-Issued OpenID Provider (SIOP) protocol flow. In this syntax type, the sub (subject) claim value in the ID Token issued by the Self-Issued OP is the base64url encoded representation of the JWK thumbprint of the key in the sub_jwk (subject key) claim. The sub_jwk claim is included in the Self-Issued Response and contains the public key used by the Self-Issued OP to sign the ID Token.

This syntax type allows the Relying Party (RP) to verify the signature on the ID Token using the JWK thumbprint of the key, without having to retrieve the JWK set from the Self-Issued OP. The JWK thumbprint is a compact representation of the public key and can be used as a unique identifier for the key. This approach reduces the size of the SIOP request and response messages and simplifies the processing for the RP.

Motivation

The JWK Thumbprint subject syntax type is a mandatory requirement for compliant implementation of the Self-Issued OpenID Provider (SIOP) protocol, as described in the SIOPv2 specification. Therefore, adding support for this subject syntax type will ensure that this SIOP library is compliant with the latest standards and can interoperate with other compliant SIOP implementations.

Resources

SIOPv2 JWK Thumbprint
JWK Thumbprint specification

To-do List

  • Add the JWKThumprint SubjectSyntaxType as a method of signing the SIOP Response (by the Provider)
  • Add ability for the RelyingParty to validate a SIOP Response

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.