#574 Add a JwtBearer sample.

This commit is contained in:
Chris R 2015-12-01 14:06:12 -08:00
parent 3d8886a064
commit 17072b1417
17 changed files with 478 additions and 0 deletions

View File

@ -54,6 +54,8 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.Owin.Security.Coo
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.Owin.Security.Cookies.Interop.Test", "test\Microsoft.Owin.Security.Cookies.Interop.Test\Microsoft.Owin.Security.Cookies.Interop.Test.xproj", "{73E8E654-A2AC-4848-95F3-EB55512F6C39}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "JwtBearerSample", "samples\JwtBearerSample\JwtBearerSample.xproj", "{D399B84F-591B-4E98-92BA-B0F63E7B6957}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -298,6 +300,18 @@ Global
{73E8E654-A2AC-4848-95F3-EB55512F6C39}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{73E8E654-A2AC-4848-95F3-EB55512F6C39}.Release|x86.ActiveCfg = Release|Any CPU
{73E8E654-A2AC-4848-95F3-EB55512F6C39}.Release|x86.Build.0 = Release|Any CPU
{D399B84F-591B-4E98-92BA-B0F63E7B6957}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D399B84F-591B-4E98-92BA-B0F63E7B6957}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D399B84F-591B-4E98-92BA-B0F63E7B6957}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{D399B84F-591B-4E98-92BA-B0F63E7B6957}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{D399B84F-591B-4E98-92BA-B0F63E7B6957}.Debug|x86.ActiveCfg = Debug|Any CPU
{D399B84F-591B-4E98-92BA-B0F63E7B6957}.Debug|x86.Build.0 = Debug|Any CPU
{D399B84F-591B-4E98-92BA-B0F63E7B6957}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D399B84F-591B-4E98-92BA-B0F63E7B6957}.Release|Any CPU.Build.0 = Release|Any CPU
{D399B84F-591B-4E98-92BA-B0F63E7B6957}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{D399B84F-591B-4E98-92BA-B0F63E7B6957}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{D399B84F-591B-4E98-92BA-B0F63E7B6957}.Release|x86.ActiveCfg = Release|Any CPU
{D399B84F-591B-4E98-92BA-B0F63E7B6957}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -323,5 +337,6 @@ Global
{2755BFE5-7421-4A31-A644-F817DF5CAA98} = {4D2B6A51-2F9F-44F5-8131-EA5CAC053652}
{21A56E78-31DE-4868-9778-7E4DBE2A4E35} = {4D2B6A51-2F9F-44F5-8131-EA5CAC053652}
{73E8E654-A2AC-4848-95F3-EB55512F6C39} = {7BF11F3A-60B6-4796-B504-579C67FFBA34}
{D399B84F-591B-4E98-92BA-B0F63E7B6957} = {F8C0AA27-F3FB-4286-8E4C-47EF86B539FF}
EndGlobalSection
EndGlobal

View File

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.Props" Condition="'$(VSToolsPath)' != ''" />
<PropertyGroup Label="Globals">
<ProjectGuid>d399b84f-591b-4e98-92ba-b0f63e7b6957</ProjectGuid>
<RootNamespace>JwtBearerSample</RootNamespace>
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">..\..\artifacts\obj\$(MSBuildProjectName)</BaseIntermediateOutputPath>
<OutputPath Condition="'$(OutputPath)'=='' ">..\..\artifacts\bin\$(MSBuildProjectName)\</OutputPath>
</PropertyGroup>
<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
</PropertyGroup>
<ItemGroup>
<DnxInvisibleContent Include="bower.json" />
<DnxInvisibleContent Include=".bowerrc" />
<DnxInvisibleContent Include="package.json" />
</ItemGroup>
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>

View File

@ -0,0 +1,25 @@
{
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:42023",
"sslPort": 0
}
},
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"environmentVariables": {
"ASPNET_ENVIRONMENT": "Development"
}
},
"web": {
"commandName": "web",
"environmentVariables": {
"Hosting:Environment": "Development"
}
}
}
}

View File

@ -0,0 +1,104 @@
using System;
using System.Collections.Generic;
using System.IO;
using Microsoft.AspNet.Builder;
using Microsoft.AspNet.Hosting;
using Microsoft.AspNet.Http;
using Microsoft.Extensions.DependencyInjection;
using Newtonsoft.Json.Linq;
namespace JwtBearerSample
{
public class Startup
{
// Shared between users in memory
public IList<Todo> Todos { get; } = new List<Todo>();
// This method gets called by the runtime. Use this method to add services to the container.
// For more information on how to configure your application, visit http://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app)
{
// Simple error page to avoid a repo dependency.
app.Use(async (context, next) =>
{
try
{
await next();
}
catch (Exception ex)
{
if (context.Response.HasStarted)
{
throw;
}
context.Response.StatusCode = 500;
await context.Response.WriteAsync(ex.ToString());
}
});
app.UseIISPlatformHandler();
app.UseDefaultFiles();
app.UseStaticFiles();
app.UseJwtBearerAuthentication(options =>
{
options.AutomaticAuthenticate = true;
options.AutomaticChallenge = true;
// You also need to update /wwwroot/app/scripts/app.js
options.Authority = "https://login.windows.net/tratcheroutlook.onmicrosoft.com";
options.Audience = "63a87a83-64b9-4ac1-b2c5-092126f8474f";
});
// [Authorize] would usually handle this
app.Use(async (context, next) =>
{
// Use this if options.AutomaticAuthenticate = false
// var user = await context.Authentication.AuthenticateAsync(JwtBearerDefaults.AuthenticationScheme);
var user = context.User; // We can do this because of options.AutomaticAuthenticate = true; above.
if (user?.Identity?.IsAuthenticated ?? false)
{
await next();
}
else
{
// We can do this because of options.AutomaticChallenge = true; above
await context.Authentication.ChallengeAsync();
}
});
// MVC would usually handle this:
app.Map("/api/TodoList", todoApp =>
{
todoApp.Run(async context =>
{
var response = context.Response;
if (context.Request.Method.Equals("POST", System.StringComparison.OrdinalIgnoreCase))
{
var reader = new StreamReader(context.Request.Body);
var body = await reader.ReadToEndAsync();
var obj = JObject.Parse(body);
var todo = new Todo() { Description = obj["Description"].Value<string>(), Owner = context.User.Identity.Name };
Todos.Add(todo);
}
else
{
response.ContentType = "application/json";
var json = JToken.FromObject(Todos);
await response.WriteAsync(json.ToString());
}
});
});
}
// Entry point for the application.
public static void Main(string[] args) => WebApplication.Run<Startup>(args);
}
}

View File

@ -0,0 +1,8 @@
namespace JwtBearerSample
{
public class Todo
{
public string Description { get; set; }
public string Owner { get; set; }
}
}

View File

@ -0,0 +1,27 @@
{
"version": "1.0.0-*",
"compilationOptions": {
"emitEntryPoint": true
},
"dependencies": {
"Microsoft.AspNet.Authentication.JwtBearer": "1.0.0-*",
"Microsoft.AspNet.IISPlatformHandler": "1.0.0-*",
"Microsoft.AspNet.Server.Kestrel": "1.0.0-*",
"Microsoft.AspNet.StaticFiles": "1.0.0-*"
},
"commands": {
"web": "Microsoft.AspNet.Server.Kestrel"
},
"frameworks": {
"dnx451": { },
"dnxcore50": { }
},
"exclude": [
"wwwroot",
"node_modules"
],
"publishExclude": [
"**.user",
"**.vspscc"
]
}

View File

@ -0,0 +1,28 @@
'use strict';
angular.module('todoApp', ['ngRoute','AdalAngular'])
.config(['$routeProvider', '$httpProvider', 'adalAuthenticationServiceProvider', function ($routeProvider, $httpProvider, adalProvider) {
$routeProvider.when("/Home", {
controller: "homeCtrl",
templateUrl: "/App/Views/Home.html",
}).when("/TodoList", {
controller: "todoListCtrl",
templateUrl: "/App/Views/TodoList.html",
requireADLogin: true,
}).when("/UserData", {
controller: "userDataCtrl",
templateUrl: "/App/Views/UserData.html",
}).otherwise({ redirectTo: "/Home" });
adalProvider.init(
{
instance: 'https://login.microsoftonline.com/',
tenant: 'tratcheroutlook.onmicrosoft.com',
clientId: '63a87a83-64b9-4ac1-b2c5-092126f8474f',
extraQueryParameter: 'nux=1',
// cacheLocation: 'localStorage', // enable this for IE, as sessionStorage does not work for localhost.
},
$httpProvider
);
}]);

View File

@ -0,0 +1,13 @@
'use strict';
angular.module('todoApp')
.controller('homeCtrl', ['$scope', 'adalAuthenticationService','$location', function ($scope, adalService, $location) {
$scope.login = function () {
adalService.login();
};
$scope.logout = function () {
adalService.logOut();
};
$scope.isActive = function (viewLocation) {
return viewLocation === $location.path();
};
}]);

View File

@ -0,0 +1,5 @@
'use strict';
angular.module('todoApp')
.controller('indexCtrl', ['$scope', 'adalAuthenticationService', function ($scope, adalService) {
}]);

View File

@ -0,0 +1,71 @@
'use strict';
angular.module('todoApp')
.controller('todoListCtrl', ['$scope', '$location', 'todoListSvc', 'adalAuthenticationService', function ($scope, $location, todoListSvc, adalService) {
$scope.error = "";
$scope.loadingMessage = "Loading...";
$scope.todoList = null;
$scope.editingInProgress = false;
$scope.newTodoCaption = "";
$scope.editInProgressTodo = {
Description: "",
ID: 0
};
$scope.editSwitch = function (todo) {
todo.edit = !todo.edit;
if (todo.edit) {
$scope.editInProgressTodo.Description = todo.Description;
$scope.editInProgressTodo.ID = todo.ID;
$scope.editingInProgress = true;
} else {
$scope.editingInProgress = false;
}
};
$scope.populate = function () {
todoListSvc.getItems().success(function (results) {
$scope.todoList = results;
$scope.loadingMessage = "";
}).error(function (err) {
$scope.error = err;
$scope.loadingMessage = "";
})
};
$scope.delete = function (id) {
todoListSvc.deleteItem(id).success(function (results) {
$scope.loadingMessage = "";
$scope.populate();
}).error(function (err) {
$scope.error = err;
$scope.loadingMessage = "";
})
};
$scope.update = function (todo) {
todoListSvc.putItem($scope.editInProgressTodo).success(function (results) {
$scope.loadingMsg = "";
$scope.populate();
$scope.editSwitch(todo);
}).error(function (err) {
$scope.error = err;
$scope.loadingMessage = "";
})
};
$scope.add = function () {
todoListSvc.postItem({
'Description': $scope.newTodoCaption,
'Owner': adalService.userInfo.userName
}).success(function (results) {
$scope.loadingMsg = "";
$scope.newTodoCaption = "";
$scope.populate();
}).error(function (err) {
$scope.error = err;
$scope.loadingMsg = "";
})
};
}]);

View File

@ -0,0 +1,24 @@
'use strict';
angular.module('todoApp')
.factory('todoListSvc', ['$http', function ($http) {
return {
getItems : function(){
return $http.get('/api/TodoList');
},
getItem : function(id){
return $http.get('/api/TodoList/' + id);
},
postItem : function(item){
return $http.post('/api/TodoList/',item);
},
putItem : function(item){
return $http.put('/api/TodoList/', item);
},
deleteItem : function(id){
return $http({
method: 'DELETE',
url: '/api/TodoList/' + id
});
}
};
}]);

View File

@ -0,0 +1,6 @@
'use strict';
angular.module('todoApp')
.controller('userDataCtrl', ['$scope', 'adalAuthenticationService', function ($scope, adalService) {
}]);

View File

@ -0,0 +1,3 @@
<div>
home sweet home
</div>

View File

@ -0,0 +1,24 @@
<div ng-init="populate()">
<p class="error">{{error}}</p>
<p>{{loadingMessage}}</p>
<div class="panel">
<div class="input-group">
<input ng-model="newTodoCaption" class="form-control" />
<span class="input-group-btn">
<button ng-click="add();" class="btn btn-default">Add</button>
</span>
</div>
<table class="table table-striped">
<tbody>
<tr data-ng-repeat="item in todoList">
<td>
<p data-ng-hide="item.edit">{{item.Description}}</p>
</td>
<td>
<p data-ng-hide="item.edit">{{item.Owner}}</p>
</td>
</tr>
</tbody>
</table>
</div>
</div>

View File

@ -0,0 +1,23 @@
<div>
<h3>
Id_token content
</h3>
<p>{{userInfo.userName}}</p>
<p>aud:{{userInfo.profile.aud}}</p>
<p>iss:{{userInfo.profile.iss}}</p>
<p>iat:{{userInfo.profile.iat}}</p>
<p>nbf:{{userInfo.profile.nbf}}</p>
<p>exp:{{userInfo.profile.exp}}</p>
<p>ver:{{userInfo.profile.ver}}</p>
<p>tid:{{userInfo.profile.tid}}</p>
<p>amr:{{userInfo.profile.amr}}</p>
<p>oid:{{userInfo.profile.oid}}</p>
<p>upn:{{userInfo.profile.upn}}</p>
<p>unique_name:{{userInfo.profile.unique_name}}</p>
<p>sub:{{userInfo.profile.sub}}</p>
<p>family_name:{{userInfo.profile.family_name}}</p>
<p>given_name:{{userInfo.profile.given_name}}</p>
<p>pwd_exp:{{userInfo.profile.pwd_exp}}</p>
<p>pwd_url:{{userInfo.profile.pwd_url}}</p>
</div>

View File

@ -0,0 +1,68 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Todo List: a SPA sample demonstrating Azure AD and ADAL JS</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap-theme.min.css">
</head>
<body ng-app="todoApp" ng-controller="homeCtrl" role="document">
<div class="navbar navbar-inverse navbar-fixed-top" role="navigation">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed"
data-toggle="collapse"
data-target=".navbar-collapse">
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#/Home">ADAL JS Sample</a>
</div>
<div class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li ng-class="{ active: isActive('/Home') }"><a href="#/Home">Home</a></li>
<li ng-class="{ active: isActive('/TodoList') }"><a href="#/TodoList">Todo List</a></li>
<li ng-class="{ active: isActive('/UserData') }"><a href="#/UserData" ng-show="userInfo.isAuthenticated">User</a></li>
</ul>
<ul class="nav navbar-nav navbar-right">
<li><a class="btn btn-link" ng-show="userInfo.isAuthenticated" ng-click="logout()">Logout</a></li>
<li><a class="btn btn-link" ng-hide="userInfo.isAuthenticated" ng-click="login()">Login</a></li>
</ul>
</div>
</div>
</div>
<br />
<div class="container" role="main">
<div class="row">
<div class="col-xs-10 col-xs-offset-1" style="background-color:azure">
<div class="page-header">
<h1>Todo List</h1>
</div>
<p>This sample demonstrates how to take advantage of ADAL JS for adding Azure AD authentication to your AngularJS apps.</p>
</div>
</div>
<div class="row">
<div class="col-xs-10 col-xs-offset-1">
<div ng-view class="panel-body">
</div>
</div>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.25/angular.min.js"></script>
<script src="https://code.angularjs.org/1.2.25/angular-route.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js"></script>
<script src="https://secure.aadcdn.microsoftonline-p.com/lib/1.0.0/js/adal.min.js"></script>
<script src="https://secure.aadcdn.microsoftonline-p.com/lib/1.0.0/js/adal-angular.min.js"></script>
<script src="App/Scripts/app.js"></script>
<script src="App/Scripts/homeCtrl.js"></script>
<script src="App/Scripts/userDataCtrl.js"></script>
<script src="App/Scripts/todoListCtrl.js"></script>
<script src="App/Scripts/todoListSvc.js"></script>
</body>
</html>

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.webServer>
<handlers>
<add name="httpPlatformHandler" path="*" verb="*" modules="httpPlatformHandler" resourceType="Unspecified"/>
</handlers>
<httpPlatform processPath="%DNX_PATH%" arguments="%DNX_ARGS%" stdoutLogEnabled="false" startupTimeLimit="3600"/>
</system.webServer>
</configuration>