Browse Source

Initial commit

master
Claire 3 months ago
parent
commit
f948fc07fe
  1. 20
      GoogleLogin.sln
  2. 131
      GoogleLogin/GoogleLogin.csproj
  3. 31
      GoogleLogin/GoogleLogin.csproj.user
  4. 35
      GoogleLogin/Properties/AssemblyInfo.cs
  5. 30
      GoogleLogin/Web.Debug.config
  6. 31
      GoogleLogin/Web.Release.config
  7. 14
      GoogleLogin/Web.config
  8. BIN
      GoogleLogin/bin/GoogleLogin.dll
  9. BIN
      GoogleLogin/bin/GoogleLogin.pdb
  10. 27
      GoogleLogin/footer.aspx
  11. 17
      GoogleLogin/footer.aspx.cs
  12. 26
      GoogleLogin/footer.aspx.designer.cs
  13. 94
      GoogleLogin/header.aspx
  14. 17
      GoogleLogin/header.aspx.cs
  15. 15
      GoogleLogin/header.aspx.designer.cs
  16. 67
      GoogleLogin/login.aspx
  17. 304
      GoogleLogin/login.aspx.cs
  18. 15
      GoogleLogin/login.aspx.designer.cs
  19. 4
      GoogleLogin/obj/Debug/.NETFramework,Version=v4.5.AssemblyAttributes.cs
  20. BIN
      GoogleLogin/obj/Debug/DesignTimeResolveAssemblyReferencesInput.cache
  21. 5
      GoogleLogin/obj/Debug/GoogleLogin.csproj.FileListAbsolute.txt
  22. BIN
      GoogleLogin/obj/Debug/GoogleLogin.csprojAssemblyReference.cache
  23. BIN
      GoogleLogin/obj/Debug/GoogleLogin.csprojResolveAssemblyReference.cache
  24. BIN
      GoogleLogin/obj/Debug/GoogleLogin.dll
  25. BIN
      GoogleLogin/obj/Debug/GoogleLogin.pdb
  26. 0
      GoogleLogin/obj/Debug/TemporaryGeneratedFile_036C0B5B-1481-4323-8D20-8F5ADCB23D92.cs
  27. 0
      GoogleLogin/obj/Debug/TemporaryGeneratedFile_5937a670-0e60-4077-877b-f7221da3dda1.cs
  28. 0
      GoogleLogin/obj/Debug/TemporaryGeneratedFile_E7A71F73-0F8D-4B9B-B56E-8E70B10BC5D3.cs
  29. 28
      GoogleLogin/scripts.js
  30. 74
      GoogleLogin/style.css
  31. 12
      README.md

20
GoogleLogin.sln

@ -0,0 +1,20 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Express 2012 for Web
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GoogleLogin", "GoogleLogin\GoogleLogin.csproj", "{5F0B1F6D-BAE4-4C26-B704-34CF840A387A}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{5F0B1F6D-BAE4-4C26-B704-34CF840A387A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5F0B1F6D-BAE4-4C26-B704-34CF840A387A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5F0B1F6D-BAE4-4C26-B704-34CF840A387A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5F0B1F6D-BAE4-4C26-B704-34CF840A387A}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

131
GoogleLogin/GoogleLogin.csproj

@ -0,0 +1,131 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>
</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{5F0B1F6D-BAE4-4C26-B704-34CF840A387A}</ProjectGuid>
<ProjectTypeGuids>{349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}</ProjectTypeGuids>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>GoogleLogin</RootNamespace>
<AssemblyName>GoogleLogin</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<UseIISExpress>true</UseIISExpress>
<IISExpressSSLPort />
<IISExpressAnonymousAuthentication />
<IISExpressWindowsAuthentication />
<IISExpressUseClassicPipelineMode />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Web.DynamicData" />
<Reference Include="System.Web.Entity" />
<Reference Include="System.Web.ApplicationServices" />
<Reference Include="System.ComponentModel.DataAnnotations" />
<Reference Include="System" />
<Reference Include="System.Data" />
<Reference Include="System.Core" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="System.Web.Extensions" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Drawing" />
<Reference Include="System.Web" />
<Reference Include="System.Xml" />
<Reference Include="System.Configuration" />
<Reference Include="System.Web.Services" />
<Reference Include="System.EnterpriseServices" />
</ItemGroup>
<ItemGroup>
<Content Include="header.aspx" />
<Content Include="scripts.js" />
<Content Include="login.aspx" />
<Content Include="style.css" />
<Content Include="Web.config" />
<Content Include="footer.aspx" />
</ItemGroup>
<ItemGroup>
<Compile Include="header.aspx.cs">
<DependentUpon>header.aspx</DependentUpon>
<SubType>ASPXCodeBehind</SubType>
</Compile>
<Compile Include="header.aspx.designer.cs">
<DependentUpon>header.aspx</DependentUpon>
</Compile>
<Compile Include="login.aspx.cs">
<DependentUpon>login.aspx</DependentUpon>
<SubType>ASPXCodeBehind</SubType>
</Compile>
<Compile Include="login.aspx.designer.cs">
<DependentUpon>login.aspx</DependentUpon>
</Compile>
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="footer.aspx.cs">
<DependentUpon>footer.aspx</DependentUpon>
<SubType>ASPXCodeBehind</SubType>
</Compile>
<Compile Include="footer.aspx.designer.cs">
<DependentUpon>footer.aspx</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<None Include="Web.Debug.config">
<DependentUpon>Web.config</DependentUpon>
</None>
<None Include="Web.Release.config">
<DependentUpon>Web.config</DependentUpon>
</None>
</ItemGroup>
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<Import Project="$(VSToolsPath)\WebApplications\Microsoft.WebApplication.targets" Condition="'$(VSToolsPath)' != ''" />
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v10.0\WebApplications\Microsoft.WebApplication.targets" Condition="false" />
<ProjectExtensions>
<VisualStudio>
<FlavorProperties GUID="{349c5851-65df-11da-9384-00065b846f21}">
<WebProjectProperties>
<UseIIS>True</UseIIS>
<AutoAssignPort>True</AutoAssignPort>
<DevelopmentServerPort>0</DevelopmentServerPort>
<DevelopmentServerVPath>/</DevelopmentServerVPath>
<IISUrl>http://localhost:50002/</IISUrl>
<NTLMAuthentication>False</NTLMAuthentication>
<UseCustomServer>False</UseCustomServer>
<CustomServerUrl>
</CustomServerUrl>
<SaveServerSettingsInUserFile>False</SaveServerSettingsInUserFile>
</WebProjectProperties>
</FlavorProperties>
</VisualStudio>
</ProjectExtensions>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

31
GoogleLogin/GoogleLogin.csproj.user

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<LastActiveSolutionConfig>Debug|Any CPU</LastActiveSolutionConfig>
</PropertyGroup>
<ProjectExtensions>
<VisualStudio>
<FlavorProperties GUID="{349c5851-65df-11da-9384-00065b846f21}">
<WebProjectProperties>
<StartPageUrl>
</StartPageUrl>
<StartAction>CurrentPage</StartAction>
<AspNetDebugging>True</AspNetDebugging>
<SilverlightDebugging>False</SilverlightDebugging>
<NativeDebugging>False</NativeDebugging>
<SQLDebugging>False</SQLDebugging>
<ExternalProgram>
</ExternalProgram>
<StartExternalURL>
</StartExternalURL>
<StartCmdLineArguments>
</StartCmdLineArguments>
<StartWorkingDirectory>
</StartWorkingDirectory>
<EnableENC>False</EnableENC>
<AlwaysStartWebServerOnDebug>True</AlwaysStartWebServerOnDebug>
</WebProjectProperties>
</FlavorProperties>
</VisualStudio>
</ProjectExtensions>
</Project>

35
GoogleLogin/Properties/AssemblyInfo.cs

@ -0,0 +1,35 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("GoogleLogin")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("GoogleLogin")]
[assembly: AssemblyCopyright("Copyright © 2013")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("c912d57c-9055-4ff4-a6d7-917cf96ef38a")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Revision and Build Numbers
// by using the '*' as shown below:
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

30
GoogleLogin/Web.Debug.config

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- For more information on using web.config transformation visit http://go.microsoft.com/fwlink/?LinkId=125889 -->
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
<!--
In the example below, the "SetAttributes" transform will change the value of
"connectionString" to use "ReleaseSQLServer" only when the "Match" locator
finds an attribute "name" that has a value of "MyDB".
<connectionStrings>
<add name="MyDB"
connectionString="Data Source=ReleaseSQLServer;Initial Catalog=MyReleaseDB;Integrated Security=True"
xdt:Transform="SetAttributes" xdt:Locator="Match(name)"/>
</connectionStrings>
-->
<system.web>
<!--
In the example below, the "Replace" transform will replace the entire
<customErrors> section of your web.config file.
Note that because there is only one customErrors section under the
<system.web> node, there is no need to use the "xdt:Locator" attribute.
<customErrors defaultRedirect="GenericError.htm"
mode="RemoteOnly" xdt:Transform="Replace">
<error statusCode="500" redirect="InternalError.htm"/>
</customErrors>
-->
</system.web>
</configuration>

31
GoogleLogin/Web.Release.config

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- For more information on using web.config transformation visit http://go.microsoft.com/fwlink/?LinkId=125889 -->
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
<!--
In the example below, the "SetAttributes" transform will change the value of
"connectionString" to use "ReleaseSQLServer" only when the "Match" locator
finds an attribute "name" that has a value of "MyDB".
<connectionStrings>
<add name="MyDB"
connectionString="Data Source=ReleaseSQLServer;Initial Catalog=MyReleaseDB;Integrated Security=True"
xdt:Transform="SetAttributes" xdt:Locator="Match(name)"/>
</connectionStrings>
-->
<system.web>
<compilation xdt:Transform="RemoveAttributes(debug)" />
<!--
In the example below, the "Replace" transform will replace the entire
<customErrors> section of your web.config file.
Note that because there is only one customErrors section under the
<system.web> node, there is no need to use the "xdt:Locator" attribute.
<customErrors defaultRedirect="GenericError.htm"
mode="RemoteOnly" xdt:Transform="Replace">
<error statusCode="500" redirect="InternalError.htm"/>
</customErrors>
-->
</system.web>
</configuration>

14
GoogleLogin/Web.config

@ -0,0 +1,14 @@
<?xml version="1.0"?>
<!--
For more information on how to configure your ASP.NET application, please visit
http://go.microsoft.com/fwlink/?LinkId=169433
-->
<configuration>
<system.web>
<compilation debug="true" targetFramework="4.5" />
<httpRuntime targetFramework="4.5" />
</system.web>
</configuration>

BIN
GoogleLogin/bin/GoogleLogin.dll

diff.bin_not_shown

BIN
GoogleLogin/bin/GoogleLogin.pdb

diff.bin_not_shown

27
GoogleLogin/footer.aspx

@ -0,0 +1,27 @@
 <form action="login.aspx" onsubmit="deleteAllCookies();">
<input type="submit" value="Delete Cookies" />
</form>
<% } %>
<hr />
<%
string path = @"C:\certs\";
if (hasWriteAccessToFolder(path))
{
Response.Write("<div class='good'>Access to " + path + " appears to be okay, but " +
"check permissions if certificate caching doesn't work.</div>");
}
else
{
Response.Write("<div class='bad'>Access to " + path + " is not verified. Check permissions on that folder!</div>");
}
%>
<hr />
<div class="footer">
This sample application is &copy; A Better Geek. While the source code is freely available for use in your applications,<br />
please <b>do not distribute this sample application</b>. Instead, please
<a href="http://blog.abettergeek.com/?p=529">link to the original source</a>.
</div>

17
GoogleLogin/footer.aspx.cs

@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace GoogleLogin
{
public partial class WebForm1 : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
}
}
}

26
GoogleLogin/footer.aspx.designer.cs diff.generated

@ -0,0 +1,26 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace GoogleLogin
{
public partial class WebForm1
{
/// <summary>
/// form1 control.
/// </summary>
/// <remarks>
/// Auto-generated field.
/// To modify move field declaration from designer file to code-behind file.
/// </remarks>
protected global::System.Web.UI.HtmlControls.HtmlForm form1;
}
}

94
GoogleLogin/header.aspx

@ -0,0 +1,94 @@
 <h2>Google OAuth2 Demonstration v1.0</h2>
<p>
This is an example application implementing Google OAuth2 authentication for a C# web application.
This software is provided AS IS by A Better Geek and is solely for testing purposes. Please remember
to test all code thoroughly in your own application before moving to production.
</p>
<p>
Some notes:
</p>
<ul>
<li>When viewing the source code of this ASPX page, please focus on the code between the "SAMPLE CODE" commented lines.</li>
<li>Click the "Delete Cookies" button when you're done testing, or to reset your saved client ID and client secret.</li>
<li>The code behind page is heavily commented to aid in your own understanding of the authentication process.</li>
<li>For your reference, this sample project is based on the following A Better Geek articles:
<ul>
<li>
<a href="http://blog.abettergeek.com/web-development/getting-started-google-authentication-using-oauth2-and-asp-net-c/"
target="_blank">
Part one: Getting Started
</a>
</li>
<li>
<a href="http://blog.abettergeek.com/web-development/logging-in-google-authentication-using-oauth2-and-asp-net-c/"
target="_blank">
Part two: Logging In
</a>
</li>
<li>
<a href="http://blog.abettergeek.com/web-development/validating-integrity-google-authentication-using-oauth2-and-asp-net-c/"
target="_blank">
Part three: Validating Integrity
</a>
</li>
</ul>
</li>
</ul>
<hr />
<% if (Request.Cookies["client_id"] == null && Request.Form["client_id"] == null && Session["loggedin"] != "yes") { %>
<p class="alert">
You are seeing this message because you have not configured the application yet. Setting the below
parameters will create local browser cookies storing each parameter. In order to protect your application's
client secret, please remember to <b>clear the cookies for this page when you are done testing</b>!
</p>
<p>
If you haven't already done so, you need to first go to <a href="http://code.google.com/apis/console" target="_blank">Google's API Console</a>
and create your application. Note down your <b>client id</b> and <b>client secret</b> for use in the below form.
You also need to use the URL of this page, including the port number for localhost, as the <b>redirect URI</b> for
your application.
</p>
<form id="setCookies" name="setCookies" method="post" action="login.aspx" onsubmit="return validateForm();">
<div class="row">
<div class="label">
<label for="client_id">Client ID:</label>
</div>
<div class="input">
<input type="text" name="client_id" id="client_id" />
</div>
</div>
<div class="row">
<div class="label">
<label for="client_secret">Client secret:</label>
</div>
<div class="input">
<input type="text" name="client_secret" id="client_secret" />
</div>
</div>
<div class="row">
<div class="label">
<label for="redirect_uri">Redirect URI:</label>
</div>
<div class="input">
<input type="text" name="redirect_uri" id="redirect_uri" readonly="readonly" value="<%=Request.Url.AbsoluteUri%>" />
</div>
</div>
<div class="row">
<div class="label"></div>
<div class="submit">
<input type="submit" id="submitForm" value="Save Settings" />
</div>
</div>
</form>
<% } else if (Request.Cookies["client_id"] == null && Request.Form["client_id"] != null) { %>
<%
//write POSTDATA to cookies
Response.Cookies["client_id"].Value = Request.Form["client_id"];
Response.Cookies["client_secret"].Value = Request.Form["client_secret"];
Response.Cookies["redirect_uri"].Value = Request.Form["redirect_uri"];
Response.Redirect(Request.Form["redirect_uri"]);
%>
<% } else { %>

17
GoogleLogin/header.aspx.cs

@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace GoogleLogin
{
public partial class header : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
}
}
}

15
GoogleLogin/header.aspx.designer.cs diff.generated

@ -0,0 +1,15 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace GoogleLogin {
public partial class header {
}
}

67
GoogleLogin/login.aspx

@ -0,0 +1,67 @@
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="login.aspx.cs" Inherits="GoogleLogin.login" %>
<%
//if the user logs out, this clears the session and reloads the page.
if (Request.QueryString["logout"] == "yes")
{
Session.Clear();
Response.Redirect("/login.aspx");
}
%>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>Google OAuth2 Authentication Demo, by A Better Geek</title>
<link rel="stylesheet" href="style.css" />
<script type="text/javascript" src="scripts.js"></script>
</head>
<body>
<!-- #include file="header.aspx" -->
<!-- BEGIN SAMPLE CODE -->
<%
string code = Request.QueryString["code"];
if (code != null && Session["loggedin"] != "yes")
{
//if there is a "code" value in the querystring and no login session present
//then log in using Google
string client_id = Request.Cookies["client_id"].Value;
string client_secret = Request.Cookies["client_secret"].Value;
string redirect_uri = Request.Cookies["redirect_uri"].Value;
string grant_type = "authorization_code";
//the state token in the return URL needs to be verified first.
string gState = Request["state"];
if (gState == Convert.ToString(Session["state"]))
{
string gurl = "code=" + code + "&client_id=" + client_id +
"&client_secret=" + client_secret + "&redirect_uri=" + redirect_uri + "&grant_type=" + grant_type;
Response.Write(GoogleLogin(gurl));
}
else
{
Response.Write("<div class='bad'>Please start over. If you're seeing this message, the session ID verification failed.</div>");
}
}
else if (Session["loggedin"] == "yes")
{
Response.Write("<div class='good'>Already logged in. You can <a href='?logout=yes'>logout</a> if you'd like.</div>");
}
else
{
%>
<h2>
<a href="https://accounts.google.com/o/oauth2/auth?client_id=<%=Request.Cookies["client_id"].Value%>&
response_type=code&scope=openid%20email&state=<%=Session["state"]%>&
redirect_uri=<%=Request.Cookies["redirect_uri"].Value%>">Login With Google</a>
</h2>
<%
Response.Write("<div class='alert'>Not logged in.</div>");
}
%>
<!-- END SAMPLE CODE -->
<!-- #include file="footer.aspx" -->
</body>
</html>

304
GoogleLogin/login.aspx.cs

@ -0,0 +1,304 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
//these are additional libraries needed beyond the default ones
//Reference: http://blog.abettergeek.com/?p=472#part2
using System.IO;
using System.Net;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Web.Script.Serialization;
namespace GoogleLogin
{
public partial class login : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
//when the page loads, a random session token needs to be created
//this will be sent to Google to verify that the data hasn't been tampered with
//create a random GUID as a token for preventing CSS attacks
//this only happens if a session doesn't already exist
if (Session["state"] == null)
{
Session["state"] = Guid.NewGuid();
}
}
public string GoogleLogin(string e)
{
string responseData = "";
try
{
// variables to store parameter values
string url = "https://accounts.google.com/o/oauth2/token";
// creates the post data for the POST request
string postData = (e);
// create the POST request
HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(url);
webRequest.Method = "POST";
webRequest.ContentType = "application/x-www-form-urlencoded";
webRequest.ContentLength = postData.Length;
// POST the data
using (StreamWriter requestWriter2 = new StreamWriter(webRequest.GetRequestStream()))
{
requestWriter2.Write(postData);
}
//This actually does the request and gets the response back
HttpWebResponse resp = (HttpWebResponse)webRequest.GetResponse();
string googleAuth;
using (StreamReader responseReader = new StreamReader(webRequest.GetResponse().GetResponseStream()))
{
//dumps the HTML from the response into a string variable
googleAuth = responseReader.ReadToEnd();
}
//now that we have the responseData (which is a JSON array), we can do stuff with it.
//process JSON array
JavaScriptSerializer js = new JavaScriptSerializer();
gLoginInfo gli = js.Deserialize<gLoginInfo>(googleAuth);
//the id_token is what needs to be decoded to (a) verify it and (b) get some basic details about the user
//gli.id_token is in three parts. the first two are Base64 encoded plaintext.
string[] tokenArray = gli.id_token.Split(new Char[] { '.' });
//tokenArray[0] is the JSON header
//tokenArray[1] is the JSON payload
//tokenArray[2] is an encrypted digital signature
//process header
JavaScriptSerializer js1 = new JavaScriptSerializer();
gLoginHeader glh = js1.Deserialize<gLoginHeader>(base64Decode(tokenArray[0]));
//verify signature based on header data - the signature is tokenArray[2]
//we need the keyID, which is glh.kid
//use header data to validate signature
//if the signature is valid, we can process the payload
//once the payload is processed, we can either add a new user or log in an existing user
if (verifySignature(glh.kid, tokenArray))
{
//process payload
JavaScriptSerializer js2 = new JavaScriptSerializer();
gLoginClaims glc = js2.Deserialize<gLoginClaims>(base64Decode(tokenArray[1]));
//we can tell the session that we're logged in
Session["loggedin"] = "yes";
responseData = "<div class='good'>You have successfully logged in using " + glc.email +
". You can <a href='?logout=yes'>logout</a> if you'd like.</div>";
}
}
catch (Exception ex)
{
//this shouldn't ever happen.
responseData = "<div class='bad'>ERROR</h1" + ex.Message + "<br />" + ex.StackTrace + "</div>";
}
return responseData;
}
public class gLoginInfo
{
public string access_token, token_type, id_token;
public int expires_in;
}
public class gTokenInfo
{
public string issuer, issued_to, audience, user_id, email, email_verified;
public int expires_in, issued_at;
}
public class gLoginHeader
{
public string alg, kid;
}
public class gLoginClaims
{
public string aud, iss, email_verified, at_hash, azp, email, sub;
public int exp, iat;
}
public string base64Decode(string data)
{
//add padding with '=' to string to accommodate C# Base64 requirements
int strlen = data.Length + (4 - (data.Length % 4));
char pad = '=';
string datapad;
if (strlen == (data.Length + 4))
{
datapad = data;
}
else
{
datapad = data.PadRight(strlen, pad);
}
try
{
System.Text.UTF8Encoding encoder = new System.Text.UTF8Encoding();
System.Text.Decoder utf8Decode = encoder.GetDecoder();
// create byte array to store Base64 string
byte[] todecode_byte = Convert.FromBase64String(datapad);
int charCount = utf8Decode.GetCharCount(todecode_byte, 0, todecode_byte.Length);
char[] decoded_char = new char[charCount];
utf8Decode.GetChars(todecode_byte, 0, todecode_byte.Length, decoded_char, 0);
string result = new String(decoded_char);
return result;
}
catch (Exception e)
{
throw new Exception("Error in base64Decode: " + e.Message);
}
}
public byte[] base64urldecode(string arg)
{
//this swaps out the characters in Base64Url encoding for valid Base64 syntax
//C# can't decode Base64 without doing this first
arg = arg.Replace('-', '+');
arg = arg.Replace('_', '/');
int strlen = arg.Length + (4 - (arg.Length % 4));
char pad = '=';
if (strlen != (arg.Length + 4))
{
arg = arg.PadRight(strlen, pad);
}
//return the Base64 decoded data as a byte array, since that's what we need for RSA
byte[] arg2 = Convert.FromBase64String(arg);
return arg2;
}
public bool verifySignature(string kid, string[] jwt)
{
//this will return TRUE if the signature is valid or FALSE if it is invalid
//if the signature is invalid, we must not accept the user's login information!
//by default, the signature isn't valid, just as a precaution
bool verified = false;
//pull out the different elements from the original JWT provided by Google
string toVerify = jwt[0] + "." + jwt[1];
string signature = jwt[2];
//the JWS is encoded in Base64 that C# doesn't like
//we need to replace some special characters and then Base64Decode this into a byte array
byte[] sig = base64urldecode(signature);
//the header and payload need to be converted to a byte array
byte[] data = Encoding.UTF8.GetBytes(toVerify);
//before we do anything else, we need to locally cache Google's public certificate, if it isn't already
cacheCertificate(kid);
//now we can validate the signature against our locally-cached certificate
//create an X509 cert from the google certificate in local cache
X509Certificate gcert = X509Certificate.CreateFromCertFile(@"C:\certs\" + kid + ".cer");
//we need to use the new X509Certificate2 subclass in order to pull the public key from the certificate
X509Certificate2 gcert2 = new X509Certificate2(gcert);
//this lets us use the public key from the cached Google certificate
using (var rsa = (RSACryptoServiceProvider)gcert2.PublicKey.Key)
{
//create a new byte array that contains a SHA256 hash of the JSON header and payload
byte[] hash;
using (SHA256 sha256 = SHA256.Create())
{
hash = sha256.ComputeHash(data);
}
//Create an RSAPKCS1SignatureDeformatter object and pass it the
//RSACryptoServiceProvider to transfer the key information.
RSAPKCS1SignatureDeformatter RSADeformatter = new RSAPKCS1SignatureDeformatter(rsa);
RSADeformatter.SetHashAlgorithm("SHA256");
//Verify the hash and return the appropriate bool value
if (RSADeformatter.VerifySignature(hash, sig))
{
verified = true;
}
else
{
verified = false;
}
}
return verified;
}
public void cacheCertificate(string kid)
{
//if the certificate ID doesn't already exist as a local certificate file, download it from Google
if (!File.Exists(@"C:\certs\" + kid + ".cer"))
{
//pull JSON certificate data from Google
string url = "https://www.googleapis.com/oauth2/v1/certs";
WebRequest request = WebRequest.Create(url);
WebResponse response = request.GetResponse();
Stream certdata = response.GetResponseStream();
string certs;
using (StreamReader sr = new StreamReader(certdata))
{
certs = sr.ReadToEnd();
}
//certs are returned as a JSON object
JavaScriptSerializer js = new JavaScriptSerializer();
//convert the JSON object into a dictionary
//pick the certificate that matches the returned key ID from Google
Dictionary<dynamic, dynamic> cts = js.Deserialize<Dictionary<dynamic, dynamic>>(certs);
string b64 = cts[kid];
//write the certificate to a file with the .cer extension, which identifies it as a digital certificate
//these are stored outside the web server filespace
//at some point the server needs to have a daily script running that deletes certificates that are more than 48 hours old
System.IO.File.WriteAllText(@"C:\certs\" + kid + ".cer", b64);
}
}
//This is just for testing that your configuration allows write access to the folder where cached certificates are stored.
//You don't need this in your own project.
public bool hasWriteAccessToFolder(string folderPath)
{
try
{
// Attempt to get a list of security permissions from the folder.
// This will raise an exception if the path is read only or do not have access to view the permissions.
System.Security.AccessControl.DirectorySecurity ds = Directory.GetAccessControl(folderPath);
return true;
}
catch (UnauthorizedAccessException)
{
return false;
}
}
}
}

15
GoogleLogin/login.aspx.designer.cs diff.generated

@ -0,0 +1,15 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace GoogleLogin {
public partial class login {
}
}

4
GoogleLogin/obj/Debug/.NETFramework,Version=v4.5.AssemblyAttributes.cs

@ -0,0 +1,4 @@
// <autogenerated />
using System;
using System.Reflection;
[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute(".NETFramework,Version=v4.5", FrameworkDisplayName = ".NET Framework 4.5")]

BIN
GoogleLogin/obj/Debug/DesignTimeResolveAssemblyReferencesInput.cache

diff.bin_not_shown

5
GoogleLogin/obj/Debug/GoogleLogin.csproj.FileListAbsolute.txt

@ -0,0 +1,5 @@
K:\Development\bitfork\project\GoogleLogin\GoogleLogin\bin\GoogleLogin.dll
K:\Development\bitfork\project\GoogleLogin\GoogleLogin\bin\GoogleLogin.pdb
K:\Development\bitfork\project\GoogleLogin\GoogleLogin\obj\Debug\GoogleLogin.csprojResolveAssemblyReference.cache
K:\Development\bitfork\project\GoogleLogin\GoogleLogin\obj\Debug\GoogleLogin.dll
K:\Development\bitfork\project\GoogleLogin\GoogleLogin\obj\Debug\GoogleLogin.pdb

BIN
GoogleLogin/obj/Debug/GoogleLogin.csprojAssemblyReference.cache

diff.bin_not_shown

BIN
GoogleLogin/obj/Debug/GoogleLogin.csprojResolveAssemblyReference.cache

diff.bin_not_shown

BIN
GoogleLogin/obj/Debug/GoogleLogin.dll

diff.bin_not_shown

BIN
GoogleLogin/obj/Debug/GoogleLogin.pdb

diff.bin_not_shown

0
GoogleLogin/obj/Debug/TemporaryGeneratedFile_036C0B5B-1481-4323-8D20-8F5ADCB23D92.cs

0
GoogleLogin/obj/Debug/TemporaryGeneratedFile_5937a670-0e60-4077-877b-f7221da3dda1.cs

0
GoogleLogin/obj/Debug/TemporaryGeneratedFile_E7A71F73-0F8D-4B9B-B56E-8E70B10BC5D3.cs

28
GoogleLogin/scripts.js

@ -0,0 +1,28 @@
function validateForm() {
var inputs = document.getElementsByTagName("input");
var list = "";
for (i = 0; i < inputs.length; i++) {
if (inputs[i].value == "") {
list = list + inputs[i].id + " must be completed.\n";
}
}
if (list != "") {
alert(list);
return false;
}
return true;
}
function deleteAllCookies() {
var cookies = document.cookie.split(";");
for (var i = 0; i < cookies.length; i++) {
var cookie = cookies[i];
var eqPos = cookie.indexOf("=");
var name = eqPos > -1 ? cookie.substr(0, eqPos) : cookie;
document.cookie = name + "=;expires=Thu, 01 Jan 1970 00:00:00 GMT";
}
}

74
GoogleLogin/style.css

@ -0,0 +1,74 @@
BODY {
margin-left: 25%;
width: 50%;
font-family: Calibri, sans-serif;
}
UL {
margin-top: -.75em;
font-size: .8em;
}
UL LI {
padding: .25em 0;
}
UL UL {
margin-top: 0em;
font-size: 1em;
}
FORM#setCookies {
overflow: hidden;
width: 400px;
margin: auto;
}
.row {
clear: both;
}
.label, .input, .submit {
height: 25px;
float: left;
}
.label {
width: 100px;
padding-right: 5px;
line-height: 25px;
text-align: right;
font-size: .8em;
}
.input, .submit {
width: 250px;
}
.input INPUT {
width: 100%;
font-family: Calibri, sans-serif;
}
.submit {
text-align: center;
}
.alert, .good, .bad {
clear: both;
margin: 1em 0.5em;
padding: 5px;
background: #ff9;
border: 1px dotted #f90;
font-size: .8em;
font-style: italic;
color: #c60;
}
.good, .bad {
font-style: normal
}
.good {
background: #cfc;
border-color: #0c3;
color: #060;
}
.bad {
background: #fcc;
border-color: #f00;
color: #900;
}
.footer {
text-align: center;
font-size: .8em;
font-style: italic;
color: #666;
}

12
README.md

@ -1,3 +1,11 @@
# DotNetOAuth2
# .NET OAuth2
A secure implementation of OAuth2 using vanilla ASP.NET C#. Requires no third-party libraries or assemblies.
A secure implementation of OAuth2 using vanilla ASP.NET C#. Requires no third-party libraries or assemblies.
**This is proof-of-concept and was developed in September 2013. I do not recommend implementing this code in a production environment without careful evaluation of the security implications.**
This project implements [OAuth2](https://oauth.net/2/), which is a standardized means of implementing third-party authentication for web applications. Securing user accounts is a serious endeavor that requires an adequate understanding of the complex security concerns around protecting users' authentication and identification information. OAuth2 (and its predecessor, OpenID) attempts to address this scenario, by making it easy for web developers to support authentication from a variety of popular online services, such as Google, Microsoft, Twitter, and Facebook. This project uses Google authentication.
My implementation relies exclusively on vanilla .NET Framework 4.5 assemblies. Although this project implements Google authentication, it should be trivial to modify the code to use other OAuth2 providers.
For a detailed breakdown of the code, check out the [wiki](/ABG/DotNetOAuth2/wiki).
Loading…
Cancel
Save