Giter Site home page Giter Site logo

brockallen.membershipreboot's Introduction

As of 2017 MembershipReboot will no longer be maintained. It has served its purpose, and ASP.NET Identity has finally caught up (and surpassed) this library in terms of security and functionality. If you are interested in taking over maintenance, let me know.


What is MembershipReboot

Join the chat at https://gitter.im/brockallen/BrockAllen.MembershipReboot

MembershipReboot is a user identity management and authentication library. It has nothing to do with the ASP.NET Membership Provider, but was inspired by it due to frustrations with the built-in ASP.NET Membership system. The goals are to improve upon and provide missing features from ASP.NET Membership. It is designed to encapsulate the important security logic while leaving most of the other aspects of account management either configurable or extensible for application developers to customize as needed.

Some of the features of MembershipReboot are:

  • single- or multi-tenant account management
  • flexible account storage design (relational/SQL or object/NoSql)
    • samples using both EF and RavenDB
  • claims-aware user identities
  • support for account registration, email verification, password reset, etc.
  • account lockout for multiple failed login attempts (password guessing)
  • extensible templating for email notifications
  • customizable username, password and email validation
  • notification system for account activity and updates (e.g. for auditing)
  • account linking with external identity providers (enterprise or social)
  • supports certificate based authentication
  • proper password storage (via PBKDF2)
    • configurable iterations
    • defaults to OWASP recommendations for iterations (e.g. 64K in year 2012)
  • two factor authentication support via mobile phone SMS messages or client certificates

The most common use case will be to integrate this into an ASP.NET or ASP.NET MVC application, though the library can also be used over a network as a service.

Getting Started with MembershipReboot

There is a core project (BrockAllen.MembershipReboot), two projects to implement the storage of the user account data (one uses EF, the other uses RavenDB), and a unit test project. There are also several sample applications demonstrating various use-cases. The best way to see MembershipReboot in action is to start with the SingleTenantWebApp from the samples folder.

MembershipReboot, Claims and Windows Identity Foundation (WIF)

MembershipReboot is intended to support modern identity management, thus it heavily uses claims and some related concepts based upon Windows Identity Foundation in .NET 4.5. If you need a primer before diving in, we suggest the following one-hour video by Dominick Baier:

Authentication & Authorization in .NET 4.5 - Claims & Tokens become the standard Model

WIF Configuration

The sample application's web.config shows proper configuration of WIF which includes both of the following:

  <configSections>
   ...
    <section name="system.identityModel"
             type="System.IdentityModel.Configuration.SystemIdentityModelSection, System.IdentityModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089" />
    <section name="system.identityModel.services"
             type="System.IdentityModel.Services.Configuration.SystemIdentityModelServicesSection, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089" />
  </configSections>

and

  <system.webServer>
   ...
    <modules>
      <add name="SessionAuthenticationModule"
           type="System.IdentityModel.Services.SessionAuthenticationModule, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
           preCondition="managedHandler" />
    </modules>
  </system.webServer>

MembershipReboot Configuration

MembershipReboot allows for some flexibility in how it manages user accounts via its SecuritySettings class. It has these properties:

  • MultiTenant (bool)
    • if the deployment is to support multi-tenant
  • DefaultTenant (string)
    • the default tenant to use if one is not provided in the various APIs
  • EmailIsUsername (bool)
    • is the identifier used for authentication intended to be an email address
  • UsernamesUniqueAcrossTenants
    • even in a multi-tenant scenario, usernames must be unique
  • RequireAccountVerification (bool)
    • requires a user's email address to be verified before then can login
  • AllowLoginAfterAccountCreation (bool)
    • can a user login after account creation, or must they be approved first
  • AccountLockoutFailedLoginAttempts (integer)
    • number of failed login attempts before the account is locked out
  • AccountLockoutDuration (TimeSpan)
    • duration an account is locked out when the AccountLockoutFailedLoginAttempts is met
  • AllowAccountDeletion (bool)
    • allow permanent account deletion in the database (or just use a "closed" flag)
  • PasswordHashingIterationCount (integer)
    • number of iterations used in password storage
    • if not specified, then the OWASP recommondations are used (dynamically based upon the current year)
  • PasswordResetFrequency (integer)
    • frequency (in number of days) a user must change their password

These settings are configurable in a .config file or in code. See the samples for examples.

Email Configuration

The default configuration uses the .NET SMTP configuration to send emails. To run the samples you must configure your own SMTP settings. Here is an example configuration for email to use the www.sendgrid.com service:

  <system.net>
    <mailSettings>
      <smtp deliveryMethod="Network" from="[email protected]">
        <network 
          host="smtp.sendgrid.net" port="587" 
          userName="mySendgridUsername" password="mySendgridUsername"
          enableSsl="true"
        />
      </smtp>
    </mailSettings>
  </system.net>

TIP: If you are using Google mail, it is very easy to create multiple email addresses for testing. If your real email address is [email protected] then you can also use [email protected] or [email protected] and Google mail sends them all to [email protected].

Database Configuration

The samples use Entity Framework and SQL Server Compact. You don't need to and can configure your own repository (such as SQL Azure, or even a NoSql database). See the samples for an example.

Documentation

There is preliminary documentation here.

brockallen.membershipreboot's People

Contributors

amd989 avatar astroukovram avatar brockallen avatar calebkiage avatar ccrowhurstram avatar chengwhynot avatar christianacca avatar codingoutloud avatar davidchristiansen avatar doublej42 avatar girtsl avatar gitter-badger avatar henrikwm avatar ircnelson avatar jdiamond avatar jeremycade avatar johnmcavinue avatar kebin avatar keizof avatar lgoudriaan avatar mikecole avatar rallman avatar rvdkooy avatar simongh avatar sobieck avatar svenbit avatar swellfr avatar szelzz avatar trentapple avatar vmlf01 avatar

Stargazers

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

Watchers

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

brockallen.membershipreboot's Issues

Extra connection strings in Web.config need some explanation

There are three connection strings provided in the sample app. It appears only the connection string named MembershipReboot is used and the others are provided to demonstrate formatting for different types of database targets. This is useful, but can be confusing if you don't realize that only one is being used. The others could be commented out and commented, perhaps.

Can't get AntiForgeryConfig to resolve

Hello Brock,

For some reason I can't get AntiForgeryConfig to resolve in global.asax.cs in my sample project.

I noticed when searching the web there were some other weird issues with AntiForgeryConfig that others had been having.

I have using statements for both System.Web.Helpers and System.Web.Webpages (getting desperate here) and nothing I can do will get it to resolve and build.

What do I need to check that might be environment or configuration related?

Thanks

Here is the line of code.

AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimTypes.NameIdentifier;

Resharper errors flagged in Web.config

In web.config Resharper shows 2 red flags both with 'Cannot resolve symbol:

type="System.IdentityModel.Services.SessionAuthenticationModule, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"

and down in the sharedListeners section:
filter type=""

The program still compiles and runs ok so it must be just a resharper thing (as per: http://youtrack.jetbrains.com/issue/RSRP-337095) but I notice that in the References section of the class project the Assembly name 'System.identitymodel.services' shows in lower case (save for leading 'S'). Seems strange. Wondering if this is just my machine or others are seeing this?

Updating claims doesn't take effect immediately

string gender = System.Security.Claims.ClaimsPrincipal.Current.Claims.GetValue(ClaimTypes.Gender);
// gender = ""

account.RemoveClaim(ClaimTypes.Gender);
account.AddClaim(ClaimTypes.Gender, "male");
userAccountService.Update(account);

gender = System.Security.Claims.ClaimsPrincipal.Current.Claims.GetValue(ClaimTypes.Gender);
// gender = ""

On a new request following the above the gender will be set correctly... but until the end of the current request it will retain it's original value.

The claims fail be updated in the current ClaimsPrinciple until the next request.

Add tracing

We need tracing for all account activity for debug and audit purposes.

Emailing Verification after registration not working?

Hello,

The email capability is not working after signup when it is supposed to send out a verification email. I am trying to trace it back from the MVC app to MembershipReboot but am having problems.

It doesn't seem to ever use SMTPMessageDelivery only IMessageDelivery unless I am missing something.

This is understandably causing a hiccup in moving forwards.

Thanks for the help. I love project, especially the MVC project. Trying to get proper membership and claims going.

How to integrate user accounts from multiple identity providers?

QUESTION (brainstorming):

I think this will be a common scenario: a public-facing web application wishes to offer users to use EITHER a custom (site-specific) account (MembershipReboot) OR use an account they already have from Google, Facebook, etc.

Where should the app keep account information from the external providers?

One possibility is to use something separate like FederatedUserAccounts, and model from there. But wouldn't it be nice to be able to also use the UserClaims table we already have to flexibly model provided data, esp since it can vary from provider to provider?

Another possibility is to extend the MR UserAccounts table to include an IdentityProvider column that can be used to differentiate the provider. MR would have its own value for this and the MR functions could ignore accounts with foreign IdentityProvider values. For non-MR this would be populated with the http://schemas.microsoft.com/accesscontrolservice/2010/07/claims/identityprovider value.

Are there other good ways to model this?

Question: How do I use it?

I'm probably missing something really simple but I've installed it and so far can't see how I use it.

The sample application (single tenant) won't build for me and I'm finding the code difficult to follow.

Do you have any simple tutorials for getting a very basic "hello world" with login, register and reset password/forgot password working?

SecuritySettings GetAppSettings throws error when parsing to TimeSpan

I added the AccountLockoutDuration in my web.config appSettings, but now the SecuritySettings class explodes when T GetAppSettings<T>(string, T) is called for that setting.

Apparently it doesn't have a way to parse from string to TimeSpan when runnning the following line:

return (T)Convert.ChangeType(val, typeof(T))

I supposed you can add code to check if typeof(T) is TimeSpan, then do a TryParse instead. For now, this also worked but falls back to default if it cannot convert:

var converter = TypeDescriptor.GetConverter(typeof(T));
return converter.CanConvertFrom(val.GetType()) ?
      (T)converter.ConvertFrom(val) : defaultValue;

how to use roles? there is no role table.

I'm new to claims based security and I just downloaded the sample applications. I'm wondering how can I have roles management which is not covered in the samples.

another question what's the best way to link the UserAccounts table to my own UserProfile table?

How to have login with email or username?

A lot of sites now offer the ability to log in to them using either username or email address interchangeably.

Is this possible in MembershipReboot? If so, what would be the best way to go about it?

MultiTenant does not initialize properly

Running a fresh installation of MembershipReboot results in a call to

UserAccountService.CreateAccount(username, password, email)

Which is a pass-thru to another overloaded CreateAccount method, passing through all parameters and adding a null value for tenant.

If SecuritySettings.Instance.MultiTenant is true then the tenant value will be null. Then there is a check to ensure it is not null:

if (String.IsNullOrWhiteSpace(tenant)) throw new ArgumentException("tenant");

Under a multi-tenant configuration, this exception will be triggered always.

Add optional ability to associate TenantId

Common use case is to track TenantId for multi-tenant users. This would fit naturally as an additional (optional) claim associated with the user. This would be a good feature to offer in the box so that it could be handled without the need for custom code.

[MVC] LoginController does not account for multi-tenant on SignIn

FYI, there are a few nits still in converting things to multi-tenant in the sample.

LoginController.cs makes a straight shot to authSvc.SignIn(model.Username); ireespective of the MultiTenant status.

I am running things in a multi-tenant setup (so the structure is really appreciated) - will let you know what else I find along the way.

I am doing the tenant based on the subdomain and using the following function:

private static string GetSubdomain(Uri uri)
{
string subdomain = null;
if (uri.HostNameType == UriHostNameType.Dns)
{
string host = uri.Host;
int lastIndex = host.LastIndexOf('.') - 1;
if (lastIndex > 0)
{
if ((lastIndex = host.LastIndexOf('.', lastIndex)) != -1)
subdomain = host.Substring(0, lastIndex);
}
}
return subdomain;
}

Running on Hosted Web Server

I wanted to run the example on ARVIXE (shared hosting) and make sure it would work. It hit an error when trying to login. This may not be an issue due to the IIS version being 7.x

I did find this solution and was wondering if there was a way to fix it to use certificate based encryption?
http://stackoverflow.com/questions/5464146/is-it-possible-to-run-wif-without-loaduserprofile-true

System.InvalidOperationException: ID1074: A CryptographicException occurred when attempting to encrypt the cookie using the ProtectedData API (see inner exception for details). If you are using IIS 7.5, this could be due to the loadUserProfile setting on the Application Pool being set to false. ---> System.Security.Cryptography.CryptographicException: The data protection operation was unsuccessful. This may have been caused by not having the user profile loaded for the current thread's user context, which may be the case when the thread is impersonating. at System.Security.Cryptography.ProtectedData.Protect(Byte[] userData, Byte[] optionalEntropy, DataProtectionScope scope) at System.IdentityModel.ProtectedDataCookieTransform.Encode(Byte[] value) --- End of inner exception stack trace --- at System.IdentityModel.ProtectedDataCookieTransform.Encode(Byte[] value) at System.IdentityModel.Tokens.SessionSecurityTokenHandler.ApplyTransforms(Byte[] cookie, Boolean outbound) at System.IdentityModel.Tokens.SessionSecurityTokenHandler.WriteToken(XmlWriter writer, SecurityToken token) at System.IdentityModel.Tokens.SessionSecurityTokenHandler.WriteToken(SessionSecurityToken sessionToken) at System.IdentityModel.Services.SessionAuthenticationModule.WriteSessionTokenToCookie(SessionSecurityToken sessionToken) at BrockAllen.MembershipReboot.ClaimsBasedAuthenticationService.SignIn(UserAccount account, String method) at BrockAllen.MembershipReboot.ClaimsBasedAuthenticationService.SignIn(UserAccount account) at BrockAllen.MembershipReboot.Mvc.Areas.UserAccount.Controllers.LoginController.Index(LoginInputModel model) at lambda_method(Closure , ControllerBase , Object[] ) at System.Web.Mvc.ActionMethodDispatcher.Execute(ControllerBase controller, Object[] parameters) at System.Web.Mvc.ReflectedActionDescriptor.Execute(ControllerContext controllerContext, IDictionary2 parameters) at System.Web.Mvc.ControllerActionInvoker.InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary2 parameters) at System.Web.Mvc.Async.AsyncControllerActionInvoker.<>c__DisplayClass42.b__41() at System.Web.Mvc.Async.AsyncResultWrapper.<>c__DisplayClass81.<BeginSynchronous>b__7(IAsyncResult _) at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResult1.End() at System.Web.Mvc.Async.AsyncControllerActionInvoker.EndInvokeActionMethod(IAsyncResult asyncResult) at System.Web.Mvc.Async.AsyncControllerActionInvoker.<>c__DisplayClass37.<>c__DisplayClass39.b__33() at System.Web.Mvc.Async.AsyncControllerActionInvoker.<>c__DisplayClass4f.b__49() at System.Web.Mvc.Async.AsyncControllerActionInvoker.<>c__DisplayClass37.b__36(IAsyncResult asyncResult) at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResult`1.End() at System.Web.Mvc.Async.AsyncControllerActionInvoker.EndInvokeActionMethodWithFilters(IAsyncResult asyncResult) at System.Web.Mvc.Async.AsyncControllerActionInvoker.<>c__DisplayClass25.<>c__DisplayClass2a.b__20() at System.Web.Mvc.Async.AsyncControllerActionInvoker.<>c__DisplayClass25.b__22(IAsyncResult asyncResult)

Linked accounts

Are linked accounts (google, facebook, etc) desirable? Something like what simple membership does?

UserClaims Table Type & Value

Hi Brock,

I am creating a new membership table, and I can't figure how the UserClaims table is supposed to work. Say for example that my User has a list of Books and a list of Blogs, so should I be creating a UserBooks & UserBlogs table with fluent API, or are we adding the value of the lists to the UserClaims table, where the Value field is the value of the BookName or BlogName? If so, then shouldn't that be BlogId and BookId instead, and what happens when a Book Entry is deleted, how do I make sure the row is deleted since there is no ON Delete Cascade. My apology it the question seems very dumb, I am just confused how this list of Claims can work when we are working with other table in db as part of the value?

Unit Tests began tossing error on 'System.Web.Helpers.Crypto'

My initial work with the recently added unit tests had everything working (passing) with the code as distributed. Somewhere along the lines my adaptations (having absolutely _nothing to do with Crypto stuff) kicked up enough digital dust that the project started tossing a compiler error (pasted below)

I'm sure the error is not indicated any sort of flaw in the code but rather 'just one of those things' that occasionally happens when working the asp.net framework. Only posting it here cuz I'm betting it's an obvious solution and just in case others encounter the same.

:

Error 7 The type 'System.Web.Helpers.Crypto' exists in both 'c:\Users\foo\Documents\Visual Studio 2012\Projects\bar\BrockAllen.MembershipReboot\bin\Debug\BrockAllen.MembershipReboot.dll' and 'c:\Users\foo\Documents\Visual Studio 2012\Projects\bar\packages\Microsoft.AspNet.WebPages.2.0.20710.0\lib\net40\System.Web.Helpers.dll' C:\Users\foo\Documents\Visual Studio 2012\Projects\bar\BrockAllen.MembershipReboot.Test\Services\Crypto\CryptoHelperTests.cs 84 43 BrockAllen.MembershipReboot.Test

pointing at:

System.Web.Helpers.Crypto.HashPassword("pass");

in:

PasswordWithoutPrefix_StillValidatesWithDefault() and IncorrectPrefix_DoesNotVerify()

I've commented out those 2 test methods and am able to compile again and my most immediate use case will let me leave them untested. Still, it'd be nice to understand and fix the issue.

I'd tried the usual troubleshooting of cleaning out folders thoroughly - beyond that....any thoughts?

thx

Shouldn't password stretching be done on the client?

I'm fairly new to the concept of password stretching and might be missing something. From what I understand the point of password stretching is to slow down a potential attack by increasing the time required to generate the hash. Shouldn't it be done on the client instead of the server, because if we do it on the server we are just opening it up to a potential DNS attack?

Team Foundation Service Automated build fails

I have just added the Membership Reboot into my MVC 4 project and now the automated builds on Team Foundation Service fail advising that the referenced file can not be found (see bottom of the question). I have told the system to copy the file into the project but just wondering if there is any way to resolve this.

Builds work fine on my development machine and the project runs fine.

Please advise if you need any further info:

This is the first error it reports:
ResolveAssemblyReferences:
Primary reference "BrockAllen.MembershipReboot".
C:\Windows\Microsoft.NET\Framework64\v4.0.30319\Microsoft.Common.targets(1605,5): warning MSB3245: Could not resolve this reference. Could not locate the assembly "BrockAllen.MembershipReboot". Check to make sure the assembly exists on disk. If this reference is required by your code, you may get compilation errors.

Password rotation

Add password rotation feature. Must at least check against current password for similar.

Multi-Tenancy with the same user.

[edited a bit as tenant is definitely needed in the UserAccount table as it is used everythere]

I'm struggling a bit with one area of multi-tenancy which is still an issue I need to solve for.

In my case a user/id could actually belong to more than one tenant... I guess this would mean either a primary key in the UserAccount table which is a composite between id and tenant? [which I am looking at implementing] It is too much of an architecture change to just moving the tenant value as a claim...

Your thoughts? Thanks

Suggestion: Add a property on UserAccount to force password change at next login

It would be very useful for an admin to be able to force a user to change their password at next login.

Example use case:
Admin changes users password for them (I know they can request reset themselves... they didn't.. they are dumb)

At this point the user should really be forced to change their password to something that even the admin doesn't know when they next log in.

The reason: Most admins will use the same default password for all such password resets.

Also, giving the admin a button they can press to force a user to change their password would be nice too.

Using with Thinktecture IdentityServer v2

Do you happen to have any samples of how this can work with Thinktecture IdentityServer v2?

An example would help me bring things all together in a more concrete way.

Thanks.

Allow users to change email when EmailIsUsername is true

Use Case: Our app is targeted towards corporate employees where it is perfectly acceptable for the user to use their corporate email to make purchases. However, when they leave their company, they may no longer have access to their email. In addition, not having the ability to change username allows employer to have access to their transactions using the email (for example via reset password).

These transactions belong to user and not employer. It is insufficient to request that user create a new account as all prior history will be lost if email cannot be changed and is used as the username.

Id for the UserAccount field?

Your thoughts on the following are greatly appreciated.

A thought, and something I think I am implementing... I think it would be good to have an Id field in the UserAccount table for a database identifier that will stay fixed, even if they update their username or email.

Primary use case is for referencing the user in other normal application tables - when a fixed id would be needed. I don't think it needs to be added much anywhere since it wouldn't need to be updated or reference much in the UserAccount interactions since you would always have username and tenant context there.

Places I have have seen which would need updating:

UserAccount.cs
public virtual int Id { get; private set; }

UserAccountService,cs
//Not needed in create as Id will be auto created since it is an identity
public virtual UserAccount GetById(int id)

InitialMigration.cs (SqlCe and SqlServer)
Id = c.Int(nullable: false, identity: true),

.PrimaryKey(t => new { t.Id });
(make Id the primarykey so it can link to all the other tables)
(In UserClaims keep that PrimaryKey, ForeignKey relationship)

Am I missing any other places where it would need to be?

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.