Giter Site home page Giter Site logo

cas-mfa's People

Contributors

alexkolson avatar apetro avatar bzfoster avatar chasegawa avatar dbhankins avatar dima767 avatar fcsdbm avatar gitter-badger avatar gsu-mforest avatar mmoayyed avatar mschwager avatar msidd avatar savvasmisaghmoayyed 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

Watchers

 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

cas-mfa's Issues

Support default authn methods

Presently, if an RP does not specify an authn method the CAS protocol takes effect and continues as usual. While this works fine and is the expected behavior, we may also want to define a default opt-in authn method, so that CAS may always function in MFA mode.

Bad behavior on comma-containing displayname value

I think this is regarding the display name feature merged today: when display name value contains comma, behaves badly. As in, would like to use a Lastname, Firstname style entry ("Moayyed, Misagh") but that doesn't work.

Closure: Deploy changes over to Heroku

This should be the last change/issue to tackle before we close out this milestone.

The requirement is to merge the existing master branch with heroku and push upstream, so the code becomes demoable to a certain extent.

Also, tag the master at the end of this milestone to create a baseline, move master to a new branch and march forward :)

Remember multiple previously fulfilled required authentication methods, and honor these on subsequent login attempts

This is the in-the-guts part of supporting multiple different "authn_method"s (another issue tracks the routing to potentially multiple sub-flows part of the story).

The Authentication in the TGT should wrap or otherwise reflect the potentially multiple Authentications achieved in the single sign-on session. modeling as a Set of Strings representing the labels of fulfilled authentication methods as the value of an Authentication attribute is promising.

"This isn't me!" control on additional authentication factor prompt

When CAS prompts for an additional authentication factor, besides the option of providing providing that factor, the end-user should also have the option to click a button or link indicating "That's not me!" -- telling CAS to log out and re-start the login flow giving them the opportunity to log in, first using username and password, as themselves.

This addresses the abandoned single sign-on session case where the walking-up-user attempts to log in to something that requires an additional authentication factor and finds themselves prompted to add an authentication factor to someone else's single sign-on session.

Specification of multiple active authn method retrievers

The present functionality only allows to configure one authn method retriever, while in fact several may be active to retrieve data. This issue only addresses the concern from an architectural POV, where we need to refactor a number of components to allow a "list" of retrievers take effect and figure out the authn method, rather than just one. We'll still be assuming, for this issue only, that only one of these retrievers in the list can just produce data and the desired method, but nonetheless, the code should be looping through a list of configurable retrievers to find the authn method.

broken handling of incorrect one-time-password

Summary: Getting one-time password wrong yields CAS is Unavailable screen.

Affected code: current Master branch

Steps to reproduce:

  1. specify a valid authn_method at CAS login
  2. correctly complete the normal basic username and password prompt
  3. provide any incorrect answer to the additional authentication factor prompt
  4. observe the erroneous CAS is Unavailable screen instead of enjoying a suitable error message and opportunity to re-try the one-time password prompt

What should have happened:
Sensible error message included in a re-render of the one-time password prompt, with an opportunity to try again at providing a correct one-time password.

required authentication method available to login JSP

79ec6b7 backed out login JSP EL usage that only works under Tomcat 7 but not under Tomcat 6 (whereas Tomcat 6 support remains required).

Institute behavior at the Java / web flow layer to digest the required authentication method, if any, and make it available as a simple pre-digested requiredAuthenticationMethod String (conversation scoped?) that the JSP can condition on. null if no particular method required; the String identifier for the required method if one is required.

User attributes for MFA may need to be translated.

When we resolve principal attributes, and are in the process of figuring out the attribute value that would specify the mfa, we need to provide a pluggable API so that an adopter would be able to manipulate the attribute value, to the right subflow.

So an institution might have an attribute value for mfa that is "very_strong", but that would need be translated at the CAS level to be "really_strong". By default we are going to assume attribute values do match and will have a no-op translator, but we should have the API that attempts to do so, just to be nice :)

Unrecognized authentication method should error on login

When /cas/login has an authn_method parameter with an unrecognized value, CAS should present a suitable end-user-facing error experience, since CAS will be unable to walk the user through fulfilling the not-understood authentication method requirement and so cannot issue an ST suitable for the requesting service.

(Current behavior is to ignore the authn_method which vends the user an ST that CAS expects will not be sufficient for login to the requesting service.)

Implement configurable ranking algorithm of authn methods

Presently, all authentication methods are created equal. These are simply tokens, that CAS would remember as what's been fulfilled during the authentication context.

More, we currently are assuming that while authentication methods can be specified via multiple channels (params, SvcReg, Attr), these channels cannot all at the same time indicate the mfa requirement. In order to support multiple means of conveying MFA triggers, and have them all produce possible values and play nice with each other at the same time, we need to come up with methods of how each authentication ranks and compares against another.

Use case might be, that:

  • A user may want opt-in MFA, through a parameter for method X
  • The service registry defines the RP with the requirement of using method Y
  • At the same time, once the user authenticates, his/her configured attribute indicates that method Z should be used.

So:

  • We'll need a way to configurable describe X, Y and Z in the configuration.
  • We'll need ways to define how X Y and Z compare against each other (which is higher than what)
  • We'll also need an evaluation engine to parse through and evaluate what the ultimate and can-be-satisfied method should be.

The engine needs to be smart enough to know that:

  • If service registry for RP requires method X
  • If user attribute for the principal requires Y
  • ...and Y > X, use Y instead
  • ...communicate back to the app that X was fulfilled since what's what the app really wants to know anyway.

Some more background info:

  • The automatic configuration of ranking removes the need to allow the user to choose the authentication method, if more than one is available. We simply calculate it and move on.
  • What's more, the "first factor" is always per the current design user-name and password. method only determine what happens after that.
  • Furthermore, to limit scope, we are only implementing 2FA really. That is, you authenticate with username-password always by default, and the authentication method describes what should happen next in the 2nd step...and then we're done. While the codebase should allow chaining of multiple authentication methods (such that you'd login with uid-password, then certs, then duo, then your blood sample!), we are not going that far.

JavaScript injection vulnerability on additional authentication factor prompt pages

In casMfaLoginView.jsp

<spring:message 
  code="service.mfa.service.mfa.inprogress.message"
  arguments="${service.authenticationMethod},${service}" />

will include the raw value of the service parameter in the JSP. The Adversary might place a markup snippet in the service URL parameter in an attempt to inject JavaScript, as in

https://localhost:/cas/login?authn_method=strong_two_factor&service=http://www.unicon.net"><script>alert('SECURITYALERT!!!')</script>

and this JSP will include that JavaScript in the page content delivered to the browser.

This is a potential SSO session stealing exploit.

MFA should take into account renew=true

This is an existing bug that I found out during testing:

I have an SSO session already that was backed by an MFA authn method. I then submit the following request to cas:

/cas/login?authn_method=some_valid_id&service=https://myservice&renew=true

Per protocol, I expect to see the login screen. I do not.

Error compile MFA Core

We are having problems trying to build with Maven. Am I missing something ? The source code is about 6 months old.

Thank you

cas-mfa-java/src/main/java/net/unicon/cas/mfa/MultiFactorAuthenticationProtocolValidationSpecification.java:34:83: Expected @param tag for 'validateProxyAuthenticationRequests'.

[INFO] ------------------------------------------------------------------------
[INFO] Reactor Summary:
[INFO]
[INFO] CAS Multifactor Authentication .................... SUCCESS [ 1.354 s]
[INFO] CAS MFA Core ...................................... FAILURE [ 4.845 s]
[INFO] CAS MFA Web Application ........................... SKIPPED
[INFO] CAS MFA Overlay ................................... SKIPPED
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE

Specification of authn method via user attributes

The current MFA solution only supports JIT selection of authn methods, via request parameters sent by the relying party (CAS client). We would also want to support the retrieval of the authn method via user attributes. The flow would be as such:

When credentials are submitted and user has authenticated, a principal is formed with a collection of user attributes. Via the configuration, provide a way for the adopter to specify which attribute value should be examined for the authentication method specified for the user. Based on the value, re-route the login flow.

If the attribute value is blank, ignore the MFA requirement and proceed normally. do not disable MFA, but simply comply with what may have been specified through other means and channels.
If the attribute value matches a flow id that CAS does not support, be sure to prompt and halt the login flow. Do not error out or crash, but simply explain the situation to the user via a friendly error message.

User attributes always take precedence over service registry settings or request parameters. Selection order for MFA requirements is always, user attribute first, then svc registry, then request parameters.

Accompany the PR with test cases and documentation as needed

Wrap the MFA authentication object to include the entire chain

The constructed authentication object that is placed into the final MFA-enabled TGT needs to not depend on a particular authentication object in the chain, but rather, should wrap the entire chain inside it.

CF. https://github.com/Unicon/cas-mfa/blob/master/cas-mfa-java/src/main/java/net/unicon/cas/mfa/authentication/principal/MultiFactorCredentials.java#L98

This is to support the usecase of remembering and fulfilling all authentication methods (which there may be multiples of) on validation responses.

Add support for multi-valued MFA user attributes

We are currently assuming that the user attribute, which indicates the required MFA context is a single valued attribute. In reality, this may contain a list/set of attribute values. We'll need to support those cases as well.

In the event that more than one value is specified, we'll need to plug the set into the ranking engine to figure out which is the appropriate method to use.

Note that this is simply kept here for recording keeping and documentation. We dont plan to address this for the time being.

Authentication-method-specific sub-flow aware of and enforces matching of Principal

Where a specific authentication method is required, CAS first requires the basic username-and-password login. The result of that basic login is CAS knows about an authenticated Principal.

Here's the punchline: sub-flows modeling particular authentication methods need to be aware of, reflect, and enforce that the additional authentication they are obtaining authenticates that same Principal.

Logging in as Andrew in the main flow and then successfully presenting Misagh's one-time-password isn't evidence for a more strongly authenticated Andrew.

Likewise, the user shouldn't have to type their username again in the sub-flow's prompt for additional credential when CAS is already aware of who the user is supposed to be.

ticket validation response fails to include authentication method

For a request to (chrome)

view-source:https://localhost:8443/cas/serviceValidate?service=http://www.unicon.net&ticket=ST-12-mYUbtWrLGiF5P4dVDL5p-cas.example.org&authn_method=strong_two_factor

I get

<cas:serviceResponse xmlns:cas='http://www.yale.edu/tp/cas'>
  <cas:authenticationSuccess>
  <cas:user>casuser</cas:user>



  </cas:authenticationSuccess>
</cas:serviceResponse>

Whereas the response should have included the authentication method.

Supports multiple authn_methods, each modeled as a sub-flow (but at most one required method for any given service login)

Currently, the webflow branching logic will always navigate to the mfa state, regardless of the requested authentication method. While the build supports having multiple methods in place, a global redirection to one mfa state makes it difficult to handle another scheme of authentication handlers for a newly configured authn method.

The thought is to, instead of mapping the flow to one mfa event, map the mfa branching to a state that is tied to the authentication method. The map would be based off of the authentication method string, such that for instance the flow would be able to switch to mfa_strong_two_factor instead of just mfa.

Demonstrate functionality, by having at least another authn method in the config.

Tighten differences in login-webflow.xml

differences in cas-mfa-web/src/main/webapp/WEB-INF/login-webflow.xml that don't seem related to mfa. Specifically, items related to lppe in the cas 3.5.2 baseline file are missing from cas-mfa-web/src/main/webapp/WEB-INF/login-webflow.xml.

Change pom version from 0.0.1 to 1.0.0-M2

To be consistent with the versioning scheme already established i.e. there already exists v1.0.0-M1 tag/version. So let's adopt this scheme going forward and march towards 1.0.0-M2. @mmoayyed any objections?

Rename the mfaArgumentExtractor class

So, since we have now the great RegisteredServiceAttributeMultiFactorAuthenticationArgumentExtractor.java, lets do the same with the MultifactorArgumentExtractor class and rename it so it makes more sense as to what it really does. It serves us fine when we only had to support request parameters, but now that we are adding more, we should clarify the intention of the class.

Validation should consider ranks of authn methods

Currently, the validation logic only and simply compares authn methods and in case of a mismatch, forces reauthenticaton. This should not be the case with the new ranking algorithm in place. Validation should take into account the current method's rank, as well as the rank of what's requested, do a comparison and let the higher rank win. if the result of that comparison requires a re-authentication, then so be it. otherwise, no prompt and proceed normally.

What should be returned back to the application, is "what they requested" and not "what CAS has fulfilled". If app requests A, and CAS has fulfilled Y, and Y > A, then CAS should simply just not prompt and let the app know that A has been fulfilled.

Externalize other hardcoded messages in the UI

Messages that are intended for customization should really come from message bundles:

<div id="msg" class="info">
  <h2>Multifactor Authentication In Progress</h2>
  <h4>Authentication method is set to [${service.authenticationMethod}].</h4>
  <h4>Authentication is requested by [${service}].</h4>
</div>

and...

<div id="msg" class="info">
        <h2>This service requires a specific authentication method in addition to username and password.</h2>
        <strong>The additional required authentication method is [${requiredAuthenticationMethod}].
        After successfully providing username and password, you will be prompted for this additional authentication
            factor.</strong>
    </div>

There may be other instances.

Add LICENSE

Clearly license as Apache 2 identical to Apereo CAS license as per Evergreen RFP terms.

Pull request offered addressing: #13

Modify codebase to migrate to CAS4

Just for record keeping: CAS4.1 is now available. While the current codebase is functional based on CAS 3.5.x, we should nonetheless at some point take the effort to upgrade the overlay to v4 and figure out what we can drop, etc.

Tagged as icebox for now.

JPA ServiceRegistry entries not persisted with cas-mfa

When using org.jasig.cas.services.JpaServiceRegistryDaoImpl for the serviceRegistryDao bean, entries created via the services manager web application are not persisting to the underlying database. I'm wondering if there is a @transactional annotation missing somewhere?

I have used the following configuration with cas-mfa (replacing <cas:json-services-registry/> in cas-mfa-web/src/main/webapp/WEB-INF/deployerConfigContext.xml with the following:

<bean id="serviceRegistryDao" class="org.jasig.cas.services.JpaServiceRegistryDaoImpl"
          p:entityManagerFactory-ref="entityManagerFactory" />

<!-- This is the EntityManagerFactory configuration for Hibernate -->
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
                <property name="generateDdl" value="true"/>
                <property name="showSql" value="true" />
            </bean>
        </property>
        <property name="jpaProperties">
            <props>
                <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
                <prop key="hibernate.hbm2ddl.auto">update</prop>
            </props>
        </property>
</bean>

<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>

<tx:annotation-driven transaction-manager="transactionManager"/>

<bean
        id="dataSource"
        class="org.apache.commons.dbcp.BasicDataSource"
        p:driverClassName="com.mysql.jdbc.Driver"
        p:url="jdbc:mysql://myhost.edu:3306/cas_mfa?autoReconnect=true"
        p:password="password"
        p:username="user" />

When I attempt to add an entry in the services manager UI, I get the following log entries, and find that no row has been inserted in the RegisteredServiceImpl table:

2013-09-03 17:02:50,303 DEBUG [org.jasig.cas.services.web.RegisteredServiceSimpleFormController] - <Removing form session attribute [org.jasig.cas.services.web.RegisteredServiceSimpleFormController.FORM.registeredService]>
2013-09-03 17:02:50,311 DEBUG [org.jasig.cas.services.web.RegisteredServiceSimpleFormController] - <No errors -> processing submit>
2013-09-03 17:02:50,312 DEBUG [org.hibernate.internal.SessionImpl] - <Opened session at timestamp: 13782529703>
2013-09-03 17:02:50,312 TRACE [org.hibernate.internal.SessionImpl] - <Setting flush mode to: AUTO>
2013-09-03 17:02:50,312 TRACE [org.hibernate.internal.SessionImpl] - <Setting cache mode to: NORMAL>
2013-09-03 17:02:50,317 TRACE [org.hibernate.engine.spi.IdentifierValue] - <ID unsaved-value: -1>
2013-09-03 17:02:50,317 TRACE [org.hibernate.event.internal.AbstractSaveEventListener] - <Transient instance of: org.jasig.cas.services.Registered
ServiceImpl>
2013-09-03 17:02:50,318 TRACE [org.hibernate.event.internal.DefaultMergeEventListener] - <Merging transient instance>
2013-09-03 17:02:50,328 TRACE [org.hibernate.event.internal.AbstractSaveEventListener] - <Saving [org.jasig.cas.services.RegisteredServiceImpl#<null>]>
2013-09-03 17:02:50,339 TRACE [org.hibernate.event.internal.WrapVisitor] - <Wrapped collection in role: org.jasig.cas.services.AbstractRegisteredService.allowedAttributes>
2013-09-03 17:02:50,340 TRACE [org.hibernate.engine.spi.ActionQueue] - <Adding an EntityIdentityInsertAction for [org.jasig.cas.services.RegisteredServiceImpl] object>
2013-09-03 17:02:50,342 TRACE [org.hibernate.engine.spi.ActionQueue] - <Adding insert with no non-nullable, transient entities: [EntityIdentityInsertAction[org.jasig.cas.services.RegisteredServiceImpl#<delayed:0>]]>
2013-09-03 17:02:50,342 TRACE [org.hibernate.engine.spi.ActionQueue] - <Adding resolved non-early insert action.>
2013-09-03 17:02:50,346 TRACE [org.hibernate.action.internal.UnresolvedEntityInsertActions] - <No unresolved entity inserts that depended on [[org.jasig.cas.services.RegisteredServiceImpl#<delayed:0>]]>
2013-09-03 17:02:50,347 TRACE [org.hibernate.action.internal.UnresolvedEntityInsertActions] - <No entity insert actions have non-nullable, transient entity dependencies.>
2013-09-03 17:02:50,348 TRACE [org.hibernate.internal.SessionImpl] - <Closing session>
2013-09-03 17:02:50,348 TRACE [org.hibernate.engine.jdbc.internal.LogicalConnectionImpl] - <Closing logical connection>
2013-09-03 17:02:50,348 TRACE [org.hibernate.engine.jdbc.internal.JdbcResourceRegistryImpl] - <Closing JDBC container [org.hibernate.engine.jdbc.internal.JdbcResourceRegistryImpl@2b0fe524]>
2013-09-03 17:02:50,348 TRACE [org.hibernate.engine.jdbc.internal.LogicalConnectionImpl] - <Logical connection closed>
2013-09-03 17:02:50,349 INFO [org.jasig.cas.services.web.RegisteredServiceSimpleFormController] - <Saved changes to service -1>

Using the exact same configuration with stock CAS 3.5.2 works correctly (that is, adding an entry persists to RegisteredServiceImpl).

Any ideas?

Greet user by name on additional authentication factor prompt JSPs

Demonstrate greeting the user by a user attribute on the additional authentication factor prompt JSPs.

Principal attributes should be available to these JSPs and it should be clear to CAS extension adopters how to customize the JSP page to greet the user by the institution's user attribute(s) of choice.

As in, "Welcome back, Misagh! (this isn't me!)"

(The "This isn't me" part is tracked in another Issue; this issue tracks the greeting by deployer-selected user attributes part of the story.)

Specification of authn method in service registry

The current MFA solution only supports JIT selection of authn methods, via request parameters sent by the relying party (CAS client). With support of the JSON service registry, ensure that each service definition can also specify a desired authentication method.

The existing argument extractor should be kept. If a service definition in the registry specifies an authentication method, use that exclusively, and ignore what's provided as the request parameter. If the service does not specify anything, look at the request parameter. The service registry definitions always take precedence.

Provide sufficient test cases to demonstrate the behavior. Also, update the documentation to explain the new behavior with examples, code snippets, etc.

Enforce authn_method on /cas/proxyValidate

/cas/proxyValidate must enforce a required authn_method if one is expressed by the ticket-validating relying party, but does not currently so enforce.

It is common for CAS clients to validate service tickets against `/cas/proxyValidate'

The case of actual proxy tickets is kind of interesting -- validation should traverse back to the underlying TGT to verify that the required authn_method was fulfilled.

Returning casServiceFailureView after Successful ST Validation

With the MFA extension, CAS returns the casServiceFailureView view to clients calling serviceValidate, despite the fact that the actual validation is successful. This problem began when a new service was added that has very long service URL. The symptom only presents on the second or subsequent ST validation after TGT creation. Reverting back to a non-MFA CAS demonstrates that the problem only occurs in MFA CAS. The symptoms do not occur only on requests with these very long service URL, rather, the service with very long URL puts CAS in a state where this happens with all services and a CAS restart is required to correct it. Additionally, a single instance of the very long URL will not necessarily trigger the problem. We added a few logger.debug lines to MultiFactorServiceValidateController.java and started getting a bit more info about where the problem was occurring:
2014-01-23 11:10:26,703 DEBUG [net.unicon.cas.mfa.web.MultiFactorServiceValidateController] - <ServiceTicket [ST-117-DLSkRVYvyer9tiTuEvqt-cas01.example.org] does not satisfy validation specification.>
2014-01-23 11:10:26,703 DEBUG [net.unicon.cas.mfa.web.MultiFactorServiceValidateController] - <convertedDescription is ticket ''{0}'' not recognizedand code is INVALID_TICKET>
From MultiFactorServiceValidateController.java:
if (!validationSpecification.isSatisfiedBy(assertion)) {
logger.debug("ServiceTicket [" + serviceTicketId + "] does not satisfy validation specification.");
return generateErrorView("INVALID_TICKET", "INVALID_TICKET_SPEC", null);

I may be able to correct or provide more info but I need a little assistance in where to start looking.

Devise a method for configuring authn methods as cas properties

For the most part, all a deployer cares about in the args extractor config file, is the bean supportedAuthenticationMethodsConfig. This is a mapping of methods->ranks.

Lets try and see if we can come up with a strategy to move this configuration outside the overlay and have CAS reference it, either as a cas.property or another config file. This will make it easier to add/remove methods, and will also simplify overlays because they no longer wold need the xml config file

Duo application key missing?

Duo provides all configuration pieces for the handler (secret, client key, etc) but there is no such thing as application key. Clarify in the documentation what that is and how it can be obtained.

Don't display principal identifier in additional authentication factor forms

While greeting the user is desirable and affording the user the opportunity that they are not the currently logged in user identified by CAS is important (tracked in other Issues), the principal identifier should not be experienced as part of the form itself prompting for the additional authentication factor.

Additional authentication methods participate in CAS Audit Trail

Succeeding or failing at presenting an additional authentication factor should generate entries in the CAS server audit trail log.

Successful and failed ticket validation attempts should generate audit trail log entries that reflect whether and what authentication method was fulfilled.

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.