Giter VIP home page Giter VIP logo

is-10's Introduction

AMWA IS-10 NMOS Authorization Specification

Lint Status Render Status

What does it do?

  • Allows an API server to accept or reject requests depending on what a client is authorized to do

Why does it matter?

  • Security in the control plane is essential
    • Best practice is to limit what clients can do

How does it work?

  • Control client provides credentials and gets an access token
    • Sends token with API requests
  • Based on JSON Web Tokens and OAuth 2.0
  • Encryption is a prerequisite (see BCP-003-01)

Getting started

There is more information about the NMOS Specifications and their GitHub repos at https://specs.amwa.tv/nmos.

is-10's People

Contributors

andrewbonney avatar dannymeloy avatar garethsb avatar jonathan-r-thorpe avatar lo-simon avatar neoadvancedtechnology avatar peterbrightwell avatar prince-chrismc avatar

Stargazers

 avatar  avatar  avatar

Watchers

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

is-10's Issues

Add optional/recommended support for OIDC Client Registration management protocol

There was a desire during a recent workshop to provide for https://tools.ietf.org/html/rfc7592. Is this RFC was indicated as supported in the OAuth metadata then a client could use it to check whether it is already registered with the Authorization server. This should not be mandatory however as it isn't necessarily widely supported. Clients should fall back to an approach where they try to obtain tokens and re-register as a client if required on error.

Should IS-10 or BCP-003-02 recommend using an allow list for issuers?

I've seen guidance that the iss claim should not be blindly followed but should be subject to an allow list... e.g. see https://curity.io/resources/learn/jwt-best-practices/#5-always-check-the-issuer.

It seems to me that confirming the chain of trust from an installed root CA to the specified issuer merely ensures that the server is who it says it is, it doesn't mean that you should trust that server as an issuer of JWT for this system?

Clarify that scopes are required in the token request

Following Slack discussion yesterday...

RFC 6749 Section 3.3 allows that the "scope" is optional in a token request.

"If the issued access token scope is different from the one requested by the client, the authorization server MUST include the "scope" response parameter to inform the client of the actual scope granted. If the client omits the scope parameter when requesting authorization, the authorization server MUST either process the request using a pre-defined default value or fail the request indicating an invalid scope."

RFC 7591 Section 2 allows a dynamic client registration to include a "scope" parameter.

"String containing a space-separated list of scope values (as described in Section 3.3 of OAuth 2.0 [RFC6749]) that the client can use when requesting access tokens. The semantics of values in this list are service specific. If omitted, an authorization server MAY register a client with a default set of scopes."

Unless there's something more definitive that says that the registered scopes are supposed to become the default for that client, I think we ought to clarify that the token request needs to include (the relevant subset of) them explicitly.

[dannym-refactor] Relationship between DNS-SD discovery and RFC 8414 approach

Discovery mechanism currently defined uses DNS-SD via service type _nmos-auth._tcp. With the TXT records defined this allows URLs to be constructed of the form {proto}://{host}:{port}/x-nmos/auth/{version}. (There may be multiple such URLs for one DNS-SD service instance if multiple protocols or API versions are advertised.)

With the proposed alignment to RFC 8414, will BCP-003-02 recommend the above approach to discovering an auth server, or does the RFC 8414 /.well-known/ URL come into play? That URL isn't versioned, so I'm not sure how these specs should play nicely together.

Authorization Server Mix-Up Mitigation

IS-10 RAML says:

The issuer identifier is used to prevent authorization server mix-up attacks, as described in 'OAuth 2.0 Mix-Up Mitigation' [MIX-UP]

However, the reference id 'MIX-UP' is not defined in the RAML or elsewhere in the spec. As far as I can see it's a direct quote from https://tools.ietf.org/html/rfc8414#section-2 where MIX-UP refers to https://tools.ietf.org/html/draft-ietf-oauth-mix-up-mitigation-01.

The Authorization Implementation Guide refers to https://tools.ietf.org/html/draft-ietf-oauth-browser-based-apps-07, which in section 9.5 says:

Clients MUST use a unique redirect URI for each authorization server
used by the application. The client MUST store the redirect URI
along with the session data (e.g. along with "state") and MUST verify
that the URI on which the authorization response was received exactly
matches.

However, I don't see anything in IS-10, BCP-003-02 or the IG about using a unique redirect_uri per auth server?
Should that be required explicitly?

Or are we assuming all auth servers on the network share a common record of registered clients, and does that, or something else, mitigate this?

Clarify requirements for 'aud' JWT attribute

The docs hint that wildcarded domains should be used, but the examples include absolute URIs (with a protocol/scheme): https://github.com/AMWA-TV/nmos-authorization/blob/v1.0-dev/docs/4.4.%20Behaviour%20-%20Access%20Tokens.md#aud

In order to match the 'URI' part of StringOrURI I believe the protocol is required, but we may choose not to match that. Either way it needs to be clear what format resource servers should expect to find in the 'aud' key.

If for any reason we permitted two 'aud' formats, the testing tool should be updated to test for handling of both.

Initiate authorization for a UI-less node

How does a user initiate the authorization flow for an NMOS node which does not have a UI (e.g. pure hardware device like an SFP)?

Do we need to specify something like an /authorize endpoint which will kick start the process (redirect to the authorization server with the correct grant type and scopes)?

[dannym-refactor] auth_metadata.json

I think 'token_endpoint' should also be form part of the required elements in the auth_metadata.json scheme.

"required": ["issuer", "authorization_endpoint", "jwks_uri", "registration_endpoint", "revocation_endpoint", "response_types_supported"]
to
"required": ["issuer", "authorization_endpoint", "token_endpoint", "jwks_uri", "registration_endpoint", "revocation_endpoint", "response_types_supported"]

Port number for issuer

Where a Resource Server has no matching public key for a given token, it SHOULD attempt to obtain the missing public key via the the token iss claim as specified in RFC 8414 section 3. In cases where the Resource Server needs to fetch a public key from a remote Authorization Server it MAY temporarily respond with an HTTP 503 code in order to avoid blocking the incoming authorized request.

As iss claim does not providing the port number, where can it be obtained? Or Authorization Server should always be assigned to port 443.

Reduce lint warnings

From a recent CI run:

$ make lint
.scripts/lint.sh
Linting Markdown...
.scripts/README.md: no issues found
README.md
         1:3-1:21  warning  Use the trailing [] on reference links         no-shortcut-reference-link  remark-lint
         1:3-1:21  warning  Found reference to undefined definition        no-undefined-references     remark-lint
        7:49-7:93  warning  Don’t use literal URLs without angle brackets  no-literal-urls             remark-lint
             11:3  warning  Incorrect list-item indent: add 2 spaces       list-item-indent            remark-lint
             12:3  warning  Incorrect list-item indent: add 2 spaces       list-item-indent            remark-lint
docs/1.0. Overview.md
      31:19-31:31  warning  Use the trailing [] on reference links         no-shortcut-reference-link  remark-lint
            157:3  warning  Incorrect list-item indent: add 2 spaces       list-item-indent            remark-lint
    157:35-157:45  warning  Use the trailing [] on reference links         no-shortcut-reference-link  remark-lint
            159:3  warning  Incorrect list-item indent: add 2 spaces       list-item-indent            remark-lint
  159:101-159:108  warning  Use the trailing [] on reference links         no-shortcut-reference-link  remark-lint
            161:3  warning  Incorrect list-item indent: add 2 spaces       list-item-indent            remark-lint
            164:3  warning  Incorrect list-item indent: add 2 spaces       list-item-indent            remark-lint
docs/2.0. APIs.md
      44:46-44:65  warning  Use the trailing [] on reference links         no-shortcut-reference-link  remark-lint
      44:46-44:65  warning  Found reference to undefined definition        no-undefined-references     remark-lint
docs/3.0. Discovery.md: no issues found
docs/4.0. Behaviour.md
              7:3  warning  Incorrect list-item indent: add 2 spaces       list-item-indent            remark-lint
              8:3  warning  Incorrect list-item indent: add 2 spaces       list-item-indent            remark-lint
              9:3  warning  Incorrect list-item indent: add 2 spaces       list-item-indent            remark-lint
             10:3  warning  Incorrect list-item indent: add 2 spaces       list-item-indent            remark-lint
             11:3  warning  Incorrect list-item indent: add 2 spaces       list-item-indent            remark-lint
docs/4.1. Behaviour - Authorization Servers.md
       98:1-98:96  warning  Found unused definition                        no-unused-definitions       remark-lint
docs/4.2. Behaviour - Clients.md
      56:49-56:69  warning  Found reference to undefined definition        no-undefined-references     remark-lint
docs/4.3. Behaviour - Token Requests.md
       95:1-95:87  warning  Found unused definition                        no-unused-definitions       remark-lint
docs/4.4. Behaviour - Access Tokens.md
    124:19-124:50  warning  Use the trailing [] on reference links         no-shortcut-reference-link  remark-lint
    129:81-129:97  warning  Use the trailing [] on reference links         no-shortcut-reference-link  remark-lint
docs/4.5. Behaviour - Resource Servers.md
    73:107-73:118  warning  Use the trailing [] on reference links         no-shortcut-reference-link  remark-lint
docs/5.0. Definitions.md: no issues found
docs/6.0. Upgrade Path.md: no issues found
⚠ 25 warnings

Bulk authorization for Nodes

Simon recently brought up the issue that the user interaction in the auth code flow (for initial authorization) means that deployments of large numbers of Nodes could be very involved.

Auth servers support various mechanisms to automate client registration, but the only method to deal with this for the token flows is to use the client credentials flow. At present we don't permit this, and I believe we've discussed its use at length previously.

Adding client credentials support is relatively straightforward from a spec perspective, but it's quite a significant change. It would also place a much greater importance on authentication/authorization steps during the client registration process, which we don't currently mandate. Further to this, given that client credentials may be more dangerous if leaked, it may be worth considering things like mutual TLS in order to bind credentials to a given key pair (see https://tools.ietf.org/html/rfc8705#section-2).

This needs further discussion.

Incorrect reference?

The "Public Keys" section of "Behaviour: Resource Servers" page (https://specs.amwa.tv/is-10/releases/v1.0.0/docs/4.5._Behaviour_-_Resource_Servers.html#public-keys) includes the following text:

"it SHOULD attempt to obtain the missing public key via the the token iss claim as specified in RFC 8414 section 3"

From what I can see, RFC8414 section 3 does not describe how to obtain public keys via the token iss claim. Initially I thought this was an error in INFO-002, but it seems the INFO-002 text is copied from IS-10.

Additionally, it seems some OAuth2 implementations/code includes a "kid" parameter in the JWT header to provide the public key ID that it has been signed with. Should some reference to that be included in this section?

Client Name Duplications

When using Dynamic Client Registration, should the "Client Name" format be defined in order to prevent duplicate client names within the auth server? It would also provide a meaning to client names (where client IDs and client secrets are usually just random strings) and would provide a deterministic format for potential automation?

The client name could simply be the FQDN of the device, if the device is unlikely to change hostname. Perhaps in a more dynamic scenario where devices are likely to change domain it could perhaps be a concatenation of the product name with their MAC address to prevent registering multiple clients per device.

JWKs 'kid' may be hard to comply with in commercial auth servers

It looks like commercial auth servers may have their own internal ways of generating the 'kid' for the /jwks endpoint. This makes it harder to incorporate the pattern we have defined into it. This originally stemmed from the assumption that one auth server may be used for several things, but if something like 'NMOS' is split into its own 'realm' as seems to be relatively typical this may be less important and clients could be expected to consume all available keys.

Whilst I'm testing with Keycloak, there's a similar issue recorded against OpenAM: https://bugster.forgerock.org/jira/browse/OPENAM-10478

Examples of client registration using different grant

Would it be useful to replace register-client-post-request.json with the following 2 examples, one for using client_credentials grant and one for using authorization_code grant?

register-client-credentials-grant-client-post-request.json

{
 "client_name": "My Example Client",
 "grant_types": ["client_credentials"],
 "jwks_uri": "https://client.example.org/my_public_keys.jwks",
 "response_types": ["none"],
 "scope": "registration",
 "token_endpoint_auth_method": "private_key_jwt"
} 

register-authorization-code-grant-client-post-request.json

{
 "client_name": "My Example Client",
 "grant_types": ["authorization_code", "refresh_token"],
 "redirect_uris": [
   "https://client.example.org/callback",
   "https://client.example.org/callback2"
 ],
 "response_types": ["code"],
 "scope": "query connection",
 "token_endpoint_auth_method": "client_secret_basic"
}

Clarify the resource server behaviour while receiving missing public keys token

Let start with the following actors, 2 Authorization Servers, A and B.
A NMOS Node which is a resource server (serving the node and connection APIs) and a client (accessing the registration API).

  • Node is registered to Authorization Server A and polling it for tokens and public keys.
  • Authorization Server A does not contain Authorization Server B public keys.
  • Some service try to access Node API with Authorization Server B token.
  • Node should....

Following are the examples of how the Node could handing in this situation:

Idea 1

  1. Return 503 with a Retry-After value.
  2. Using the Token B Issuer (Authorization Server B) to fetch the authorization server metadata.
  3. Check has the Node already registered in Authorization Server B, need to somehow obtain the relevant registered client metadata associated with Authorization Server B, maybe from a database.
  4. If the relevant client metadata found.
  5. Using the client metadata to fetch the up-to-date client metadata from the Authorization Server B, if it is an OpenID Connect server.
  6. If relevant client metadata not found, or no client metdadata is retruned from the OpenID Connect server. Perform Node registration to Authorization Server B. Token will need to be authorized, i.e. user interaction required (not good!, as it could happen at anytime, and you may have no-one there to authorize it).
  7. Continuously to fetch Public Keys and Tokens from Authorization Server B in time interval.
  8. Above steps could happen again, in event of receiving token from Authorization Server A, and so on...

Idea 2

  1. Return 503 with a Retry-After value.
  2. Using the Token B Issuer (Authorization Server B) to fetch the authorization server metadata.
  3. Fetch the public keys from Authorization Server B.
  4. Append any missing keys (from Authorization Server B) to the public keys cache (from Authorization Server A).
  5. When time to re-fetch the public keys from Authorization A, Authorization B public keys are discarded.
  6. Above steps could happen again, in event of receiving the token from Authorization Server B, and so on...

Idea 3
Maybe the best solution would be updating the spec (Servers.md) using the word MUST instead of SHOULD in the following statement.
"Where multiple Authorization Servers exist in a single deployment SHOULD each host a copy of each other's public keys in order to prevent Resource Servers having to make requests to every instance."
Then Node will never received a token with missing public keys in either of the Authorization Servers. The only time Node does not has the public keys, could at the startup because it has not fetched the public keys yet. Node then return 503, and trigger to logon to the Token Issuer Server is okay.

Can anyone else think of any other idea?

After writing up the above cases I have another question, is it really a good idea to support multi-authorization servers? Let say you connected to Authorization Server A, token is authorized, user interaction. Sometime later, some network issue, or Server A is disconnected, Node is switched over to Server B, but that will again require token authorization, user interaction, and that could happen while no-one can authorize it... This is another use case for allowing client credentials flow instead of authorization code flow, for any devices or systems running 24/7.

API Interaction Sequence Diagram

Reviewing the latest version of the diagram, I noticed the "User Agent" also mentions "Broadcast Control System" for that role.

img

This seems to be causing some confusion with the NMOS Controller in the network. Would it be possible to clarify the roles?
For most Nodes the "User Agent" will be a browser, perhaps mentioning that would help.

Does the API base path require authorization?

Paths such as /x-nmos/connection/v1.1 could require a token to access, and this is what the testing tool is currently assuming. However, the claims path structure which uses paths like single/senders/* means it's not obvious how you would provide or constrain access to this base path.

Is this path 'special' in that it provides an implicit read permission if any valid token is presented to it? Does the empty string "" signify permission to access this path? Or does this path not require authorization at all?

Add recommendations around handling of redirects

As identified in BCP-003-02, it would be useful to have some generic guidance around how redirects are used on Resource Servers so that interoperability issues are minimised due to mismatched claims.

Should resource servers check the token issuer?

Posting this here so I don't forget as I still need to go back and read the RFCs again.

In a network with a single authorization server, the 'iss' in the token will always be the same and everything should be happy. If however you run more than one authorization server which happen to share keys, the value of 'iss' may vary. The spec needs to clearly indicate whether you should or should not check the value of 'iss' at the resource server end, and whether or not this value needs to be coordinated between distributed authorization server instances.

Implementation guide for Node vendors

Reading BCP-003-02 and IS-10 is very hard going for someone who is not already well versed in web security technologies (OAuth2 / JWT etc)

Understanding what areas of the IS-10/BCP-003-02 and the referenced IETF specs are needed by a node vendor (I think it is a relatively small subset), and a "quick start" guide to those areas would be very useful.

More examples of the interactions for a node would also be good.

Are clients using PKCE required to support "S256" or not?

https://github.com/AMWA-TV/nmos-authorization/blob/v1.0-dev/docs/4.3.%20Behaviour%20-%20Token%20Requests.md#grants says:

When using PKCE, clients MUST support and prefer the "S256" code_challenge_method.

But below, in https://github.com/AMWA-TV/nmos-authorization/blob/v1.0-dev/docs/4.3.%20Behaviour%20-%20Token%20Requests.md#authorization-request-and-response it says:

A code_challenge_method parameter MUST be used alongside the code_challenge parameter and SHOULD be equal to "S256" unless the client is unable to perform a SHA256 hash, in which case a value of "plain" MAY be used.

If clients are permitted to use "plain", should we make explicit somewhere that servers MUST support both "S256" and "plain"? And update the examples, some of which only have "S256" in the array.

Claim to scope mapping simplification

At present we have scopes like registration, connection etc, which map into a claim structure under x-nmos-api. Whilst it's certainly possible to create this mapping in commercial auth servers, it appears as though something like the following would be simpler. It would be worth considering a different pattern to potentially ease deployments.

Scope Name: registration
Resultant Claim: "x-nmos-registration": {...}

Scope Name: connection
Resultant Claim: "x-nmos-connection": {...}

It may equally be possible to drop the x-nmos from the claim name, or add it to the scope name to achieve consistency between scopes and claims.

Make example URLs consistent

We noticed example.com, example.org, amwa.tv... these should be consistent

And register-client-post-request.json may not be required now?

Error Codes for invalid token

In the token_error_response scheme, https://github.com/AMWA-TV/nmos-authorization/blob/v1.0-dev/APIs/schemas/token_error_response.json, we have error defined as

"error": {
      "description": "Error Type",
      "type": "string",
      "enum": ["invalid_request", "invalid_client", "invalid_grant", "unauthorized_client", "unsupported_grant_type", "invalid_scope", "unsupported_token_type"]
    },

I guess the enum set are defined by our group, but not from the RFC6750, https://tools.ietf.org/html/rfc6750#section-3.1, as it is only allowed to have invalid_request, invalid_token and insufficient_scope?

  1. Should we also have the invalid_token in our enum set, in event of incorrect issuer and incorrect audience?
  2. Maybe we should clarify what error should be used in the spec?

Schemas

  1. auth_metadata.json
    remove "jwks_uri", "revocation_endpoint", and "code_challenge_methods_supported" from required, as they are marked as optional according to the RFC8414 see https://tools.ietf.org/html/rfc8414#section-2

  2. register_client_request.json & register_client_response.json
    OpenID has added new response_types on top of oauh2.0 'code, token', they are 'id_token, id_token token, code id_token, code token, code id_token token, none'. Should they be added in the register_client_request & register_client_response schemas? I think at least 'none' is needed to be added in the register_client_response.json as it is currently returning by the keycloak auth server after a successful client registration.
    see https://openid.net/specs/oauth-v2-multiple-response-types-1_0.html
    see interesting article https://medium.com/@darutk/diagrams-of-all-the-openid-connect-flows-6968e3990660

  3. jwks_response.json
    x5c should be an array of string rather than just a string, as shown in RFC7517
    see https://tools.ietf.org/html/rfc7517#section-4.7

  4. token_response.json
    Not sure whether "Bearer" or "bearer" should be used for token_type, the keycloak auth server is using "bearer"? We can have both "Bearer" and "bearer" in the schema, and how about "BEARER"?
    see https://tools.ietf.org/html/rfc6749#section-7.1

Interaction diagram needs updating

API Interaction sequence diagram in 2.0. APIs.md precedes recent client credentials grant and should be updated, probably with node and controller diagrams.

token_error_response.json

Is there a reason token_error_response.json isn't identical to the error.json schema used in other specs? Or if OAuth 2.0 spec requires more info, could it extend error.json with additional properties?

Thanks!

Spelling of authorization and source of diagrams?

I noticed one instance of 'authorisation' rather than 'authorization' in the text - in 2.0. APIs.md.

There are several instances of it in the three diagrams. However, they are PNGs... I tried loading them into draw.io to see if the files had embedded editable objects, but that didn't seem to be the case.
I think it would be better to have editable diagrams in the repo?

[dannym-refactor] Location of Authorization Server Metadata

The current goal is to align IS-10 with RFC 8414, and as such support the method defined in RFC 8414 Section 3 for discovery of the server endpoints.

The current RAML adds a /.well-known/oauth-authorization-server endpoint. However, that is relative to the base URL defined in the RAML which is /x-nmos/auth/{version}.

I wondered if the intent was to have a top-level /.well-known/oauth-authorization-server endpoint that redirected to the one defined in the RAM L, but I can't see that RFC 8414 (or RFC 5785) allow a redirect response, suggesting only 200 OK for a successful response?

(I'm also not clear as to the use-cases for RFC 8414 multiple issuers on a host, which it describes in section 3.1, resulting in the well-known URL request being to https://api.example.com/.well-known/oauth-authorization-server/{issuer} instead. Can you explain?)

Response code for incorrect/insufficient private claim

Regarding on what error code should be used if incorrect/insufficient private claim is used. Should it be 401 (Unauthorized), or 403 (Forbidden)?

Based on https://tools.ietf.org/html/rfc6750#section-3.1, we guess it maybe 403? Any opinion?

Maybe this should be clarified in the specification.

3.1.  Error Codes

When a request fails, the resource server responds using the
appropriate HTTP status code (typically, 400, 401, 403, or 405) and
includes one of the following error codes in the response:
invalid_request
      The request is missing a required parameter, includes an
      unsupported parameter or parameter value, repeats the same
      parameter, uses more than one method for including an access
      token, or is otherwise malformed.  The resource server SHOULD
      respond with the HTTP 400 (Bad Request) status code.
invalid_token
      The access token provided is expired, revoked, malformed, or
      invalid for other reasons.  The resource SHOULD respond with
      the HTTP 401 (Unauthorized) status code.  The client MAY
      request a new access token and retry the protected resource
      request.
insufficient_scope
      The request requires higher privileges than provided by the
      access token.  The resource server SHOULD respond with the HTTP
      403 (Forbidden) status code and MAY include the "scope"
      attribute with the scope necessary to access the protected
      resource.

If the request lacks any authentication information (e.g., the client
was unaware that authentication is necessary or attempted using an
unsupported authentication method), the resource server SHOULD NOT
include an error code or other error information.

For example:
  HTTP/1.1 401 Unauthorized
  WWW-Authenticate: Bearer realm="example"

Chris McArthur (Matrox)
That is a very good question, I am under the impression a 403 would be the best respone
"We are extending the scope definition with a path permission in our private claims"

Typo in Clients Behaviour section

The first sentence of the "Client Credentials" section of the "Behaviour: Clients" says:

Confidential Clients SHOULD support authentication using a digitally signed JSON Web Token, to authenticate with the Authorization Server’s token and revocation endpoints in the manner described in Section 2.2 of RFC 7523

The text of the link at the end of this section says RFC7523, but the link itself is going to RFC7591 - I assume that's a copy/paste error?

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.