Skip to content
This repository was archived by the owner on Jun 17, 2024. It is now read-only.

Support for Visual Studio Online (VSO) authentication #1

Open
wants to merge 60 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
e463cea
Updating Graph ResourceId
tiagonmas Jul 14, 2016
d49bad2
Merge pull request #1 from matvelloso/master
tiagonmas Jul 20, 2016
4c24401
Added Visual Studio Online oAuth2 support
tiagonmas Jul 20, 2016
c4266e0
VSO authentication Implementation
tiagonmas Jul 20, 2016
5b657f2
Added files tu support VSO oAuth2 authentication
tiagonmas Jul 20, 2016
bf0e882
Furthere the implementation of VSO authentication
tiagonmas Jul 21, 2016
10e64f6
processing AuthResults and storing token
tiagonmas Jul 21, 2016
7a2dc14
Removed code that was commented. removed expiresIn auth result from V…
tiagonmas Jul 21, 2016
81ced6a
Converted some internal method from Public to Private. Added method t…
tiagonmas Jul 21, 2016
efcc51f
Reverted SampleAADv1Bot to original code. Created Sample project Ssam…
tiagonmas Jul 22, 2016
56061ed
sorted some dependencies issues
tiagonmas Jul 22, 2016
b140d03
added code to get work items from vso
tiagonmas Aug 1, 2016
ffe93dd
Implement refresh VSO token, Scopes read from webconfig. Token comman…
tiagonmas Aug 3, 2016
2f33523
Changed TokenSample to GetTokenINfo so it only shows information abou…
tiagonmas Aug 4, 2016
621ba5d
Updated way to query TFS, in order to use .Net client library instead…
tiagonmas Oct 7, 2016
eb044de
Merging with matv main rep
tiagonmas Oct 7, 2016
a928d6a
Merging with MatV main rep
tiagonmas Oct 7, 2016
5c9ee40
merging with Matt
tiagonmas Oct 7, 2016
8303bba
Revert "merging with Matt"
tiagonmas Oct 7, 2016
ee9b7b8
sorting dependencies issues after mergint with Mat
tiagonmas Oct 11, 2016
3eadc8d
continuation of MErge with Mat
tiagonmas Oct 11, 2016
1809adc
final changes resulting merge with Mat repo and changes in resumption…
tiagonmas Oct 12, 2016
5196fe9
Merged with Mat and listinf projects from TFS. Skype sigin too.
tiagonmas Oct 13, 2016
9ff0182
Update README.md
tiagonmas Oct 13, 2016
280254f
Create SampleVSOBot.md
tiagonmas Oct 13, 2016
a759942
Update SampleVSOBot.md
tiagonmas Oct 13, 2016
47b090a
Picture of VSO Sample Bot in action
tiagonmas Oct 13, 2016
7138dc0
Update SampleVSOBot.md
tiagonmas Oct 13, 2016
689dd0b
Add files via upload
tiagonmas Oct 13, 2016
f5e1e25
Delete VSOBotCapture.PNG
tiagonmas Oct 13, 2016
76d6d5b
Add files via upload
tiagonmas Oct 13, 2016
1904444
deleting image
tiagonmas Oct 13, 2016
6dd3f34
Add files via upload
tiagonmas Oct 13, 2016
dcf0428
Update SampleVSOBot.md
tiagonmas Oct 13, 2016
079777c
Update SampleVSOBot.md
tiagonmas Oct 13, 2016
fa253e1
Update SampleVSOBot.md
tiagonmas Oct 13, 2016
eab70f3
Update SampleVSOBot.md
tiagonmas Oct 13, 2016
3e519dc
Update SampleVSOBot.md
tiagonmas Oct 13, 2016
082556c
Update SampleVSOBot.md
tiagonmas Oct 13, 2016
dc862ea
Update SampleVSOBot.md
tiagonmas Oct 13, 2016
d756501
Update SampleVSOBot.md
tiagonmas Oct 13, 2016
925afab
Update SampleVSOBot.md
tiagonmas Oct 13, 2016
c882b89
Update SampleVSOBot.md
tiagonmas Oct 13, 2016
121f176
Update SampleVSOBot.md
tiagonmas Oct 13, 2016
8b1145e
Add files via upload
tiagonmas Oct 13, 2016
f91b968
Update SampleVSOBot.md
tiagonmas Oct 13, 2016
d53109b
Update SampleVSOBot.md
tiagonmas Oct 13, 2016
e804ef7
Update SampleVSOBot.md
tiagonmas Oct 13, 2016
c38ad3c
Update SampleVSOBot.md
tiagonmas Oct 13, 2016
22a864c
Update SampleVSOBot.md
tiagonmas Oct 13, 2016
79d662e
Update SampleVSOBot.md
tiagonmas Oct 13, 2016
84ee988
Update README.md
tiagonmas Oct 13, 2016
cb9ccac
Update README.md
tiagonmas Oct 13, 2016
f70d27a
Update README.md
tiagonmas Oct 13, 2016
4577e3b
Update README.md
tiagonmas Oct 13, 2016
3b24c7c
Update README.md
tiagonmas Oct 13, 2016
62979c7
Update SampleVSOBot.md
tiagonmas Oct 13, 2016
e7a0b09
Update SampleVSOBot.md
tiagonmas Oct 13, 2016
eb7e196
Reverting other 3 sample projects to same as origin to avoid conflicts
tiagonmas Oct 13, 2016
e7f3649
Merge branch 'master' of https://github.com/tiagonmas/AuthBot
tiagonmas Oct 13, 2016
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions AuthBot.sln
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SampleAADv2Bot", "SampleAAD
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OneDriveBot", "OneDriveBot\OneDriveBot.csproj", "{44BE2EEE-3F57-44D0-A298-C0967048DAE8}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SampleVSOBot", "SampleVSOBot\SampleVSOBot.csproj", "{4EE65876-C3A8-4F9D-973A-42729E205F94}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SampleAADv1Bot", "SampleAADv1Bot\SampleAADv1Bot.csproj", "{D2253234-1279-486E-8A63-D8C3424E0525}"
EndProject
Global
Expand All @@ -22,15 +24,16 @@ Global
{B8AD59D3-C36D-4E18-B504-06871001BC8D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B8AD59D3-C36D-4E18-B504-06871001BC8D}.Release|Any CPU.Build.0 = Release|Any CPU
{DFDC62CE-993B-461E-99DB-F079F46105C5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DFDC62CE-993B-461E-99DB-F079F46105C5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DFDC62CE-993B-461E-99DB-F079F46105C5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DFDC62CE-993B-461E-99DB-F079F46105C5}.Release|Any CPU.Build.0 = Release|Any CPU
{44BE2EEE-3F57-44D0-A298-C0967048DAE8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{44BE2EEE-3F57-44D0-A298-C0967048DAE8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{44BE2EEE-3F57-44D0-A298-C0967048DAE8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{44BE2EEE-3F57-44D0-A298-C0967048DAE8}.Release|Any CPU.Build.0 = Release|Any CPU
{4EE65876-C3A8-4F9D-973A-42729E205F94}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4EE65876-C3A8-4F9D-973A-42729E205F94}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4EE65876-C3A8-4F9D-973A-42729E205F94}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4EE65876-C3A8-4F9D-973A-42729E205F94}.Release|Any CPU.Build.0 = Release|Any CPU
{D2253234-1279-486E-8A63-D8C3424E0525}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D2253234-1279-486E-8A63-D8C3424E0525}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D2253234-1279-486E-8A63-D8C3424E0525}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D2253234-1279-486E-8A63-D8C3424E0525}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
Expand Down
16 changes: 7 additions & 9 deletions AuthBot/AuthBot.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,12 @@
<HintPath>..\packages\Microsoft.Identity.Client.1.0.304142221-alpha\lib\net45\Microsoft.Identity.Client.Platform.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Microsoft.IdentityModel.Clients.ActiveDirectory, Version=3.12.0.827, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.IdentityModel.Clients.ActiveDirectory.3.12.0\lib\net45\Microsoft.IdentityModel.Clients.ActiveDirectory.dll</HintPath>
<Reference Include="Microsoft.IdentityModel.Clients.ActiveDirectory, Version=3.13.5.907, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.IdentityModel.Clients.ActiveDirectory.3.13.5\lib\net45\Microsoft.IdentityModel.Clients.ActiveDirectory.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Microsoft.IdentityModel.Clients.ActiveDirectory.Platform, Version=3.12.0.827, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.IdentityModel.Clients.ActiveDirectory.3.12.0\lib\net45\Microsoft.IdentityModel.Clients.ActiveDirectory.Platform.dll</HintPath>
<Reference Include="Microsoft.IdentityModel.Clients.ActiveDirectory.Platform, Version=3.13.5.907, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.IdentityModel.Clients.ActiveDirectory.3.13.5\lib\net45\Microsoft.IdentityModel.Clients.ActiveDirectory.Platform.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Microsoft.IdentityModel.Logging, Version=1.0.0.127, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
Expand Down Expand Up @@ -105,11 +105,6 @@
</Reference>
<Reference Include="System.Net" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Net.Http.Formatting, Version=5.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.AspNet.WebApi.Client.5.2.3\lib\net45\System.Net.Http.Formatting.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System.Net.Http.WebRequest" />
<Reference Include="System.Runtime.Serialization" />
<Reference Include="System.Runtime.Serialization.Primitives, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Runtime.Serialization.Primitives.4.1.1\lib\net46\System.Runtime.Serialization.Primitives.dll</HintPath>
Expand All @@ -132,6 +127,7 @@
<Private>True</Private>
</Reference>
<Reference Include="System.Web" />
<Reference Include="System.Web.Extensions" />
<Reference Include="System.Web.Http, Version=5.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.AspNet.WebApi.Core.5.2.3\lib\net45\System.Web.Http.dll</HintPath>
<Private>True</Private>
Expand All @@ -157,11 +153,13 @@
<Compile Include="Controllers\OAuthCallbackController.cs" />
<Compile Include="Dialogs\AzureAuthDialog.cs" />
<Compile Include="Helpers\AzureActiveDirectoryHelper.cs" />
<Compile Include="Helpers\VisualStudioOnlineHelper.cs" />
<Compile Include="Models\AuthResult.cs" />
<Compile Include="Models\AuthSettings.cs" />
<Compile Include="Models\CancellationWords.cs" />
<Compile Include="Models\MemoryTokenCacheADAL.cs" />
<Compile Include="Models\MemoryTokenCacheMSAL.cs" />
<Compile Include="Models\VSOAuthResult.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
Expand Down
64 changes: 51 additions & 13 deletions AuthBot/ContextExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,42 @@ public static async Task<string> GetAccessToken(this IBotContext context, string
{
try
{
InMemoryTokenCacheADAL tokenCache = new InMemoryTokenCacheADAL(authResult.TokenCache);
var result = await AzureActiveDirectoryHelper.GetToken(authResult.UserUniqueId, tokenCache, resourceId);
authResult.AccessToken = result.AccessToken;
authResult.ExpiresOnUtcTicks = result.ExpiresOnUtcTicks;
authResult.TokenCache = tokenCache.Serialize();
context.StoreAuthResult(authResult);
//await context.PostAsync($"authToken={authResult.AccessToken}");
//await context.PostAsync($"ExpiresOnUtcTicks={authResult.ExpiresOnUtcTicks}");
if (string.Equals(authResult.authType, "vso", StringComparison.OrdinalIgnoreCase))
{
//is Token still valid ?
if (String.IsNullOrEmpty(authResult.AccessToken))
{ await context.PostAsync("GetAccessToken called but no token exists.Logon first."); }
else if (VisualStudioOnlineHelper.IsTokenExpired(authResult.ExpiresOnUtcTicks))
{
//renew token
DateTime expiredDate = new DateTime(authResult.ExpiresOnUtcTicks);
String expireDateStr = expiredDate.ToString("d/M/yyyy HH:mm:ss");
await context.PostAsync($"Your VSO credentials expired on {expireDateStr}");
authResult = await VisualStudioOnlineHelper.RefreshTokenAsync(authResult.refreshToken);
expiredDate = new DateTime(authResult.ExpiresOnUtcTicks);
expireDateStr = expiredDate.ToString("d/M/yyyy HH:mm:ss");
await context.PostAsync($"Token refreshed. New expire time: {expireDateStr}");
context.StoreAuthResult(authResult);
return authResult.AccessToken;
}

}
else {
InMemoryTokenCacheADAL tokenCache = new InMemoryTokenCacheADAL(authResult.TokenCache);
var result = await AzureActiveDirectoryHelper.GetToken(authResult.UserUniqueId, tokenCache, resourceId);
authResult.AccessToken = result.AccessToken;
authResult.ExpiresOnUtcTicks = result.ExpiresOnUtcTicks;
authResult.TokenCache = tokenCache.Serialize();
context.StoreAuthResult(authResult);
}
}
catch (Exception ex)
{
Trace.TraceError("Failed to renew token: " + ex.Message);
await context.PostAsync("Your credentials expired and could not be renewed automatically!");
await context.Logout();
await context.PostAsync("Exception in GetAccessToken: " + ex.Message);
//await context.Logout();
return null;
}
return authResult.AccessToken;
Expand Down Expand Up @@ -81,11 +105,25 @@ public static void StoreAuthResult(this IBotContext context, AuthResult authResu

public static async Task Logout(this IBotContext context)
{
context.UserData.RemoveValue(ContextConstants.AuthResultKey);
context.UserData.RemoveValue(ContextConstants.MagicNumberKey);
context.UserData.RemoveValue(ContextConstants.MagicNumberValidated);
string signoutURl = "https://login.microsoftonline.com/common/oauth2/logout?post_logout_redirect_uri=" + System.Net.WebUtility.UrlEncode(AuthSettings.RedirectUrl);
await context.PostAsync($"In order to finish the sign out, please click at this [link]({signoutURl}).");
AuthResult authResult;
string signoutURl;

if (context.UserData.TryGetValue(ContextConstants.AuthResultKey, out authResult))
{
context.UserData.RemoveValue(ContextConstants.AuthResultKey);
context.UserData.RemoveValue(ContextConstants.MagicNumberKey);
context.UserData.RemoveValue(ContextConstants.MagicNumberValidated);
if (string.Equals(authResult.authType, "vso", StringComparison.OrdinalIgnoreCase))
{
signoutURl = "https://app.vssps.visualstudio.com/_signout";
}
else
{
signoutURl = "https://login.microsoftonline.com/common/oauth2/logout?post_logout_redirect_uri=" + System.Net.WebUtility.UrlEncode(AuthSettings.RedirectUrl);
}

await context.PostAsync($"In order to finish the sign out, please click at this [link]({signoutURl}).");
}
}

}
Expand Down
10 changes: 9 additions & 1 deletion AuthBot/Controllers/OAuthCallbackController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,10 @@ public async Task<HttpResponseMessage> OAuthCallback(
{
}


// Get the resumption cookie
var resumptionCookie = new ResumptionCookie(queryParams["userId"], queryParams["botId"], queryParams["conversationId"], queryParams["channelId"], queryParams["serviceUrl"], queryParams["locale"]);

// Create the message that is send to conversation to resume the login flow
var message = resumptionCookie.GetMessage();

Expand All @@ -96,7 +98,13 @@ public async Task<HttpResponseMessage> OAuthCallback(
else if (string.Equals(AuthSettings.Mode, "b2c", StringComparison.OrdinalIgnoreCase))
{
}

else if (string.Equals(AuthSettings.Mode, "vso", StringComparison.OrdinalIgnoreCase))
{
// Exchange the Auth code with Access token
authResult = await VisualStudioOnlineHelper.GetTokenByAuthCodeAsync(code);
}


IStateClient sc = scope.Resolve<IStateClient>();

//IMPORTANT: DO NOT REMOVE THE MAGIC NUMBER CHECK THAT WE DO HERE. THIS IS AN ABSOLUTE SECURITY REQUIREMENT
Expand Down
23 changes: 19 additions & 4 deletions AuthBot/Dialogs/AzureAuthDialog.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,10 @@ public class AzureAuthDialog : IDialog<string>
private string[] scopes;
private string prompt;

public AzureAuthDialog(string resourceId)
public AzureAuthDialog(string resourceId, string prompt = "Please click to sign in: ")
{
this.resourceId = resourceId;
this.prompt = prompt;
}
public AzureAuthDialog(string[] scopes,string prompt="Please click to sign in: ")
{
Expand Down Expand Up @@ -101,9 +102,16 @@ private async Task LogIn(IDialogContext context, IMessageActivity msg, string[]
{
var resumptionCookie = new ResumptionCookie(msg);

var authenticationUrl = await AzureActiveDirectoryHelper.GetAuthUrlAsync(resumptionCookie, scopes);
String authenticationUrl;
if (string.Equals(AuthSettings.Mode, "vso", StringComparison.OrdinalIgnoreCase))
{
authenticationUrl = VisualStudioOnlineHelper.GetAuthUrlAsync(resumptionCookie);
}
else
{ authenticationUrl = await AzureActiveDirectoryHelper.GetAuthUrlAsync(resumptionCookie, scopes); }

if (msg.ChannelId == "skype" )
//if (msg.ChannelId == "skype" && !string.Equals(AuthSettings.Mode, "vso", StringComparison.OrdinalIgnoreCase))
if (msg.ChannelId == "skype" || msg.ChannelId == "emulator")
{
IMessageActivity response = context.MakeMessage();
response.Recipient = msg.From;
Expand Down Expand Up @@ -157,7 +165,14 @@ private async Task LogIn(IDialogContext context, IMessageActivity msg, string re
{
var resumptionCookie = new ResumptionCookie(msg);

var authenticationUrl = await AzureActiveDirectoryHelper.GetAuthUrlAsync(resumptionCookie, resourceId);
String authenticationUrl;
if (string.Equals(AuthSettings.Mode, "vso", StringComparison.OrdinalIgnoreCase))
{
authenticationUrl = VisualStudioOnlineHelper.GetAuthUrlAsync(resumptionCookie);
}
else
{ authenticationUrl = await AzureActiveDirectoryHelper.GetAuthUrlAsync(resumptionCookie, resourceId); }


await context.PostAsync($"You must be authenticated before you can proceed. Please, click [here]({authenticationUrl}) to log into your account.");

Expand Down
54 changes: 27 additions & 27 deletions AuthBot/Helpers/AzureActiveDirectoryHelper.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
// Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See full license at the bottom of this file.
namespace AuthBot.Helpers
{
using System;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using System;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using Microsoft.Bot.Builder.Dialogs;
using Models;

Expand All @@ -23,8 +23,8 @@ public static async Task<string> GetAuthUrlAsync(ResumptionCookie resumptionCook
Microsoft.IdentityModel.Clients.ActiveDirectory.UserIdentifier.AnyUser,
$"state={extraParameters}");
return uri.ToString();
}
}

public static async Task<string> GetAuthUrlAsync(ResumptionCookie resumptionCookie, string[] scopes)
{
var extraParameters = BuildExtraParameters(resumptionCookie);
Expand Down Expand Up @@ -90,27 +90,27 @@ public static async Task<AuthResult> GetToken(string userUniqueId, Microsoft.Ide
return authResult;
}

public static string TokenEncoder(string token)
{
return HttpServerUtility.UrlTokenEncode(Encoding.UTF8.GetBytes(token));
}
public static string TokenDecoder(string token)
{
return Encoding.UTF8.GetString(HttpServerUtility.UrlTokenDecode(token));
}
private static string BuildExtraParameters(ResumptionCookie resumptionCookie)
{
var queryString = HttpUtility.ParseQueryString(string.Empty);
queryString["userId"] = resumptionCookie.Address.UserId;
queryString["botId"] = resumptionCookie.Address.BotId;
queryString["conversationId"] = resumptionCookie.Address.ConversationId;
queryString["serviceUrl"] = resumptionCookie.Address.ServiceUrl;
queryString["channelId"] = resumptionCookie.Address.ChannelId;
queryString["locale"] = resumptionCookie.Locale ?? "en";
return TokenEncoder(queryString.ToString());
public static string TokenEncoder(string token)
{
return HttpServerUtility.UrlTokenEncode(Encoding.UTF8.GetBytes(token));
}

public static string TokenDecoder(string token)
{
return Encoding.UTF8.GetString(HttpServerUtility.UrlTokenDecode(token));
}

private static string BuildExtraParameters(ResumptionCookie resumptionCookie)
{
var queryString = HttpUtility.ParseQueryString(string.Empty);
queryString["userId"] = resumptionCookie.Address.UserId;
queryString["botId"] = resumptionCookie.Address.BotId;
queryString["conversationId"] = resumptionCookie.Address.ConversationId;
queryString["serviceUrl"] = resumptionCookie.Address.ServiceUrl;
queryString["channelId"] = resumptionCookie.Address.ChannelId;
queryString["locale"] = resumptionCookie.Locale ?? "en";

return TokenEncoder(queryString.ToString());
}
}
}
Expand Down
Loading