API is protected and client can login and call API

This commit is contained in:
Muhammad Azeez 2021-01-07 13:14:25 +03:00
commit 536c7fdb3d
41 changed files with 25757 additions and 0 deletions

458
.gitignore vendored Normal file
View File

@ -0,0 +1,458 @@
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
# User-specific files
*.rsuser
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
# Visual Studio 2015/2017 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# Visual Studio 2017 auto generated files
Generated\ Files/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUNIT
*.VisualState.xml
TestResult.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# Benchmark Results
BenchmarkDotNet.Artifacts/
# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/
# StyleCop
StyleCopReport.xml
# Files built by Visual Studio
*_i.c
*_p.c
*_h.h
*.ilk
*.meta
*.obj
*.iobj
*.pch
*.pdb
*.ipdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*_wpftmp.csproj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# Visual Studio Trace Files
*.e2e
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# JustCode is a .NET coding add-in
.JustCode
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json
# Visual Studio code coverage results
*.coverage
*.coveragexml
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# Note: Comment the next line if you want to checkin your web deploy settings,
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# The packages folder can be ignored because of Package Restore
**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
*.appx
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!?*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs
# Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
ServiceFabricBackup/
*.rptproj.bak
# SQL Server files
*.mdf
*.ldf
*.ndf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
*.rptproj.rsuser
*- Backup*.rdl
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# JetBrains Rider
.idea/
*.sln.iml
# CodeRush personal settings
.cr/personal
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config
# Tabs Studio
*.tss
# Telerik's JustMock configuration file
*.jmconfig
# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs
# OpenCover UI analysis results
OpenCover/
# Azure Stream Analytics local run output
ASALocalRun/
# MSBuild Binary and Structured Log
*.binlog
# NVidia Nsight GPU debugger configuration file
*.nvuser
# MFractors (Xamarin productivity tool) working folder
.mfractor/
# Local History for Visual Studio
.localhistory/
# BeatPulse healthcheck temp database
healthchecksdb
Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# Snowpack dependency directory (https://snowpack.dev/)
web_modules/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
.env.test
# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache
# Next.js build output
.next
out
# Nuxt.js build / generate output
.nuxt
dist
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
# Stores VSCode versions used for testing VSCode extensions
.vscode-test
# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*

View File

@ -0,0 +1,67 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using OidcSamples.TrafficPoliceApi.Data;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
namespace OidcSamples.TrafficPoliceApi.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class VehiclesController : Controller
{
private readonly ApplicationDbContext _applicationDbContext;
public VehiclesController(ApplicationDbContext applicationDbContext)
{
_applicationDbContext = applicationDbContext;
}
[HttpGet("")]
public async Task<IEnumerable<Vehicle>> Get()
{
var ownerId = UserId();
return await _applicationDbContext.Vehicles.Where(v => v.OwnerId == ownerId).ToListAsync();
}
private string UserId()
{
return User.Claims.First(c => c.Type == "sub").Value;
}
[HttpPost]
public async Task<Vehicle> Post(VehicleRequest request)
{
var vehicle = new Vehicle
{
Model = request.Model,
Color = request.Color,
LicensePlate = request.LicensePlate,
OwnerId = UserId(),
Type = (VehicleType)request.Type,
};
_applicationDbContext.Vehicles.Add(vehicle);
await _applicationDbContext.SaveChangesAsync();
return vehicle;
}
}
public class VehicleRequest
{
[StringLength(100)]
public string OwnerId { get; set; }
[StringLength(100)]
public string Model { get; set; }
[StringLength(32)]
public string Color { get; set; }
[StringLength(32)]
public string LicensePlate { get; set; }
[JsonNumberHandling(JsonNumberHandling.AllowReadingFromString)]
public int Type { get; set; }
}
}

View File

@ -0,0 +1,37 @@
using Microsoft.EntityFrameworkCore;
using System.ComponentModel.DataAnnotations;
using System.Text.Json.Serialization;
namespace OidcSamples.TrafficPoliceApi.Data
{
public class ApplicationDbContext : DbContext
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options)
{
}
public DbSet<Vehicle> Vehicles { get; set; }
}
public enum VehicleType
{
Sedan = 0,
SUV = 1,
Pickup = 2
}
public class Vehicle
{
public long Id { get; set; }
[StringLength(100)]
public string OwnerId { get; set; }
[StringLength(100)]
public string Model { get; set; }
[StringLength(32)]
public string Color { get; set; }
[StringLength(32)]
public string LicensePlate { get; set; }
public VehicleType Type { get; set; }
}
}

View File

@ -0,0 +1,63 @@
// <auto-generated />
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
using OidcSamples.TrafficPoliceApi.Data;
namespace OidcSamples.TrafficPoliceApi.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
[Migration("20210107062802_Vehicle")]
partial class Vehicle
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.UseIdentityByDefaultColumns()
.HasAnnotation("Relational:MaxIdentifierLength", 63)
.HasAnnotation("ProductVersion", "5.0.1");
modelBuilder.Entity("OidcSamples.TrafficPoliceApi.Data.Vehicle", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint")
.HasColumnName("id")
.UseIdentityByDefaultColumn();
b.Property<string>("Color")
.HasMaxLength(32)
.HasColumnType("character varying(32)")
.HasColumnName("color");
b.Property<string>("LicensePlate")
.HasMaxLength(32)
.HasColumnType("character varying(32)")
.HasColumnName("license_plate");
b.Property<string>("Model")
.HasMaxLength(100)
.HasColumnType("character varying(100)")
.HasColumnName("model");
b.Property<string>("OwnerId")
.HasMaxLength(100)
.HasColumnType("character varying(100)")
.HasColumnName("owner_id");
b.Property<int>("Type")
.HasColumnType("integer")
.HasColumnName("type");
b.HasKey("Id")
.HasName("pk_vehicles");
b.ToTable("vehicles");
});
#pragma warning restore 612, 618
}
}
}

View File

@ -0,0 +1,34 @@
using Microsoft.EntityFrameworkCore.Migrations;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
namespace OidcSamples.TrafficPoliceApi.Migrations
{
public partial class Vehicle : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "vehicles",
columns: table => new
{
id = table.Column<long>(type: "bigint", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
owner_id = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: true),
model = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: true),
color = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: true),
license_plate = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: true),
type = table.Column<int>(type: "integer", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("pk_vehicles", x => x.id);
});
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "vehicles");
}
}
}

View File

@ -0,0 +1,61 @@
// <auto-generated />
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
using OidcSamples.TrafficPoliceApi.Data;
namespace OidcSamples.TrafficPoliceApi.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
partial class ApplicationDbContextModelSnapshot : ModelSnapshot
{
protected override void BuildModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.UseIdentityByDefaultColumns()
.HasAnnotation("Relational:MaxIdentifierLength", 63)
.HasAnnotation("ProductVersion", "5.0.1");
modelBuilder.Entity("OidcSamples.TrafficPoliceApi.Data.Vehicle", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint")
.HasColumnName("id")
.UseIdentityByDefaultColumn();
b.Property<string>("Color")
.HasMaxLength(32)
.HasColumnType("character varying(32)")
.HasColumnName("color");
b.Property<string>("LicensePlate")
.HasMaxLength(32)
.HasColumnType("character varying(32)")
.HasColumnName("license_plate");
b.Property<string>("Model")
.HasMaxLength(100)
.HasColumnType("character varying(100)")
.HasColumnName("model");
b.Property<string>("OwnerId")
.HasMaxLength(100)
.HasColumnType("character varying(100)")
.HasColumnName("owner_id");
b.Property<int>("Type")
.HasColumnType("integer")
.HasColumnName("type");
b.HasKey("Id")
.HasName("pk_vehicles");
b.ToTable("vehicles");
});
#pragma warning restore 612, 618
}
}
}

View File

@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="IdentityModel" Version="5.0.0-preview.1.7" />
<PackageReference Include="IdentityModel.AspNetCore.OAuth2Introspection" Version="4.1.0" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="5.0.1" NoWarn="NU1605" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="5.0.1" NoWarn="NU1605" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="5.0.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="5.0.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="5.0.1" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="5.6.3" />
<PackageReference Include="EFCore.NamingConventions" Version="5.0.0" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,26 @@
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace OidcSamples.TrafficPoliceApi
{
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
}

View File

@ -0,0 +1,31 @@
{
"$schema": "http://json.schemastore.org/launchsettings.json",
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:55640",
"sslPort": 44305
}
},
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"launchUrl": "swagger",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"OidcSamples.TrafficPoliceApi": {
"commandName": "Project",
"dotnetRunMessages": "true",
"launchBrowser": true,
"launchUrl": "swagger",
"applicationUrl": "https://localhost:5001;http://localhost:5000",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}

View File

@ -0,0 +1,99 @@
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.OpenApi.Models;
using OidcSamples.TrafficPoliceApi.Data;
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Threading.Tasks;
namespace OidcSamples.TrafficPoliceApi
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap["sub"] = "sub";
services.AddDbContext<ApplicationDbContext>(options =>
options.UseNpgsql(Configuration.GetConnectionString("DefaultConnection"))
.UseSnakeCaseNamingConvention()
);
services.AddCors(options => options.AddPolicy("Default", builder =>
{
builder.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod();
}));
services.AddControllers();
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "OidcSamples.TrafficPoliceApi", Version = "v1" });
});
services.AddAuthorization(configure =>
{
//var policy = new AuthorizationPolicyBuilder(JwtBearerDefaults.AuthenticationScheme)
// .RequireAuthenticatedUser()
// .Build();
configure.AddPolicy("Default", configure.DefaultPolicy);
//configure.DefaultPolicy = policy;
});
// 1. Add Authentication Services
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
{
options.Authority = "https://localhost:5003";
options.Audience = "traffic-police-api";
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseSwagger();
app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "OidcSamples.TrafficPoliceApi v1"));
}
app.UseCors("Default");
app.UseHttpsRedirection();
app.UseRouting();
// 2. Enable authentication middleware
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers().RequireAuthorization();
});
}
}
}

View File

@ -0,0 +1,15 @@
using System;
namespace OidcSamples.TrafficPoliceApi
{
public class WeatherForecast
{
public DateTime Date { get; set; }
public int TemperatureC { get; set; }
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
public string Summary { get; set; }
}
}

View File

@ -0,0 +1,12 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"ConnectionStrings": {
"DefaultConnection": "User ID=postgres;Password=root;Host=localhost;Port=5432;Database=traffic_police;"
}
}

View File

@ -0,0 +1,10 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*"
}

View File

@ -0,0 +1,25 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.30717.126
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OidcSamples.TrafficPoliceApi", "OidcSamples.TrafficPoliceApi\OidcSamples.TrafficPoliceApi.csproj", "{F3B43AE8-C70F-4CC1-B8C0-CBFBA4D6E4C6}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{F3B43AE8-C70F-4CC1-B8C0-CBFBA4D6E4C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F3B43AE8-C70F-4CC1-B8C0-CBFBA4D6E4C6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F3B43AE8-C70F-4CC1-B8C0-CBFBA4D6E4C6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F3B43AE8-C70F-4CC1-B8C0-CBFBA4D6E4C6}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {B628E698-0B30-4D54-AB84-B7721D9E26CE}
EndGlobalSection
EndGlobal

23
React/.gitignore vendored Normal file
View File

@ -0,0 +1,23 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# production
/build
# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*

68
React/README.md Normal file
View File

@ -0,0 +1,68 @@
This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
## Available Scripts
In the project directory, you can run:
### `npm start`
Runs the app in the development mode.<br>
Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
The page will reload if you make edits.<br>
You will also see any lint errors in the console.
### `npm test`
Launches the test runner in the interactive watch mode.<br>
See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
### `npm run build`
Builds the app for production to the `build` folder.<br>
It correctly bundles React in production mode and optimizes the build for the best performance.
The build is minified and the filenames include the hashes.<br>
Your app is ready to be deployed!
See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
### `npm run eject`
**Note: this is a one-way operation. Once you `eject`, you cant go back!**
If you arent satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
Instead, it will copy all the configuration files and the transitive dependencies (Webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point youre on your own.
You dont have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldnt feel obligated to use this feature. However we understand that this tool wouldnt be useful if you couldnt customize it when you are ready for it.
## Learn More
You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
To learn React, check out the [React documentation](https://reactjs.org/).
### Code Splitting
This section has moved here: https://facebook.github.io/create-react-app/docs/code-splitting
### Analyzing the Bundle Size
This section has moved here: https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size
### Making a Progressive Web App
This section has moved here: https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app
### Advanced Configuration
This section has moved here: https://facebook.github.io/create-react-app/docs/advanced-configuration
### Deployment
This section has moved here: https://facebook.github.io/create-react-app/docs/deployment
### `npm run build` fails to minify
This section has moved here: https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify

21
React/cert.pem Normal file
View File

@ -0,0 +1,21 @@
-----BEGIN CERTIFICATE-----
MIIDazCCAlOgAwIBAgIUT3DoD4EzVjOECDlLV/skOjH4sNIwDQYJKoZIhvcNAQEL
BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yMTAxMDcwNjU0NTdaFw0yMjAx
MDcwNjU0NTdaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw
HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3DQEB
AQUAA4IBDwAwggEKAoIBAQCiLrQItvpfLiVGXbe/6tml5PTjKc0az7KReMFFlTc0
3KvvD/7bAfwk2RqPEVF3pUTOJ3rGbYBtYi0GdgQbgiSJuuvbUtL+Pf1RRfNR2XZq
vfcJiytsVGOvD6JiFEFTn1P+7W2zIYlO7MuqGK2f2Fjb6zeEMG/7rRGvo3YIGfx/
vQE6JTWm1HhU0vDpc3pxm2ajW5NjG++7FgoycfQ+BDoQ0XitvWj2b+jt9m2/yPdz
9ptj41mDJWvtrPyJti2/3Bprccv/+qWSZGR7Io9+31e35R85sUpFZS91zTNWyy0Z
CoSgfpqFg/3ul/ROrAYdtfP2U5TJI4GGxaqFV9QP78DrAgMBAAGjUzBRMB0GA1Ud
DgQWBBSgHQxgyIpioCFuWirLuW/ZEwVYvTAfBgNVHSMEGDAWgBSgHQxgyIpioCFu
WirLuW/ZEwVYvTAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQBF
uZzW4vwdFfeCt8rqUjDcqnz7FM1al2GccHJ7ofDQYJo8gaWuSH2SeJ11eXKDux8M
xP83Nxr4s4rh6DaSybo3wqineMmEJ2F/ikjOMdgvoeTvFV32WAn+UBcmSjvfP6C9
HSSryc13nWS31xJ2SuMHkkBeOn4ZhwiRZYcsduVFFhkBpwzQx2HD0Dr0VaEefig4
pBQy2llCFGF/u6HoGeB87Cjs0rnWguvseYl28hgsX8fkZY+IWbY8OXL+jy0g0LSc
ap9lpqst3WfyFp0UvM7EPjdsxChsSOf4KbC/aHZCqTISRcg8snEHp5Rb6zE8NP/0
k62LJlKMPn0z8dS4JEp0
-----END CERTIFICATE-----

27
React/key.pem Normal file
View File

@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAoi60CLb6Xy4lRl23v+rZpeT04ynNGs+ykXjBRZU3NNyr7w/+
2wH8JNkajxFRd6VEzid6xm2AbWItBnYEG4Ikibrr21LS/j39UUXzUdl2ar33CYsr
bFRjrw+iYhRBU59T/u1tsyGJTuzLqhitn9hY2+s3hDBv+60Rr6N2CBn8f70BOiU1
ptR4VNLw6XN6cZtmo1uTYxvvuxYKMnH0PgQ6ENF4rb1o9m/o7fZtv8j3c/abY+NZ
gyVr7az8ibYtv9waa3HL//qlkmRkeyKPft9Xt+UfObFKRWUvdc0zVsstGQqEoH6a
hYP97pf0TqwGHbXz9lOUySOBhsWqhVfUD+/A6wIDAQABAoIBAATc258LRXSHIKz8
cF77vqzfsYwCG9k68wKzmS/p6m7WUv1nAGA2rgW40LgLb+LjfK2lt2OtstUUxX7V
GQhuHYRXq2y3DgZ7e1Xtf/8rQwauTXLmzlWALkD4egjwzIiiVVVmbwyY69IG/ZBL
DyGkzf1CDzcScLkeFlKq1wYlKVH3H43rlhpP0+VvsVkPMdv3rO/N6tZGD4Dh3wY4
Pnf6RQcQVcolD7l2reLxJW7CIBJbnRFuSmtWkRNR+nii0KPxrRFMOLWRpPQlMVs3
Ho0x0C+qFghvYTHyQnIPaCsveAyeaQos4ltKSBiYT0+nABdpI39A43DOuTJIcOs4
2G4/FokCgYEA1/8sDm9zZeFUX/86dVfixOeWpP6xKqp/XlyvxHbSqLLfn8WaJ9dF
azmZWo40o7z2/gTPAVaN3PEnuU3KPCWBVWSFm3bHLzuslXF/Z5l8uqiFvhZNcNNq
mGCqMmPxqxgkIMF/aqs+0uj2DkiKvEgWeb7AfyDYPHY/tPpKlqUe8WcCgYEAwDgW
uUX6xnastpkeMV/fVl6CA5pdxVUyf4cqMMqdcFza7+dgHLSLX6jDr81zph57UzyC
MJ4RUPznhVYSFGtIRLRq83ZDrd2J4obGO0RPzbj2JxiF5o+fmo73uJDtJkZo2EPX
50OVXsJa6IES3FiGNxJlAV+TkxnzXjOqJGQ77d0CgYAQqC7/lfyyOKRHGumpLvPf
93QYmUOUZL2Zy1AKuJcaR2ETcyumMuW5lSGMaDcLAYGSqVBFbCVYiohjs+oKpWHJ
8gyTpp9JtzJh+/S1SpLh2ikmNZYXQ1aPFbVKWYbDiQMQO5vV4AriPPDsvQ9l4bFK
BjnWE/RZBU14aWzjHSy7tQKBgCJ+rk1Haq6NA4etSMbRjrHgOfLNlABivrI2HnMt
GT3d3AhrfEsKd2yOS7fT5eos1DLmy/JPm4nuKNo7zPjPG+QAgKT6V0DvXdxCFXbz
VmLzy3DOrNDoe1rwRzJfB7/zqMMOwcMl1Ltxo4DQEQNr+4IMkgXCEii24n7IRMNN
HZgdAoGBALzFDienCJmCUwpYXSLcN6ZqNYSDNQNgjwkWoRjpCYrzpRrcTxLF5o8L
Gz6QTXbkYOTZ0UaE9AlR6Aci/4o6RfucLsmbc0EvvyVbrVoX7i3rnH4HOK+RuAvj
7KlMPWiIQx4KciuUr8gHCufeCDb1RGvbRPIege8XGnJlQCf4s9sh
-----END RSA PRIVATE KEY-----

30
React/keytmp.pem Normal file
View File

@ -0,0 +1,30 @@
-----BEGIN ENCRYPTED PRIVATE KEY-----
MIIFHDBOBgkqhkiG9w0BBQ0wQTApBgkqhkiG9w0BBQwwHAQIxVNNPfT6hrwCAggA
MAwGCCqGSIb3DQIJBQAwFAYIKoZIhvcNAwcECPLxxQXt0qGZBIIEyE+8XcltU9Fv
SsFNOcp3/9saZi7sw0IIitUIF5QU2O2qG8qCQ5hU+OJkFNxwXNgBkwLvvRVS3YAH
5kkQ6S5GdoHJhw9LaSycPXYF+bOHt9IIMHmBjMKuXnX3eDL7dXwFf+EHE2HzIjN4
mjweE7eSgvuLLZfPsC6/izuCHVc66R6ZsJjQhzCakSPoGiUlZ4JcExDZA3q/NeyH
VIYy8dZ4dx1SXiLDd4r7FamRM+WxgNeCCPdZOyVlP0hlOCw1y7tH6I0oG3Ktzrj+
o/SV6e6KwQYGs2tETNTrPT0ykkMNUpGRdsx+nTZ2Dy4CVNnqwrZqVxwbVFW3kSQy
E1pP08stVbC7IbGpYWuIx2BgK4G96x+sFE8HIxUINOocVZgJQvhoGbW2EJynywZ3
tUpz9zNZTyCtKpw7iTbeLa0RVcuqJdP3YKVTYSZCOWO72TMc5WdQ1mqGwRH+i3ze
ik1YZNRBNMkLL4UZLcwuA+WfGjkWyIpyuqcqq2IlILaVCGoyWhmOWSug3ezmZTt7
eO1mgPvTUhShjlI/3DqFA2hBpq51k6MZUniKPNGcMAdyluVdw3m1MM623vocUZn6
b4ZQWldDXYTjSl3yaxjrY154z85HQJGon6Frk2kWX0PIgyNJ4S8JBYn9UvMbxp4I
bh2ZZOfIm3R4FyEX+gHpZ1NZH03NImxCK6ND7Ncz9kOPuMokjCPLuZS5XvCpLrvU
wDalfVV5TQrt1s9Ir7viZDY4xxNA6FD1rd7eA+2ZHwh0t88l7b02IMQ7hNQ0Ptn9
pt3v7xfV4w+ujRk9tFfXv9Nju/hKH6dHpPqhZP1Q1iTf1J2YX++8UCVfgNJSo4e+
ORAhvy5ZFfUeiAgtYTlbwSwDlTgGxYQ43ge7zp/B3/BgtUvvWBlZ5PNl/iTvpFUV
NeYRWB0HuzFOehDnB3ZsstjPP+ly3yoV6Xef9+aNp08HL966i41+ttNLW8r0W1yQ
taYLV3O2BtQwmQFogfaL5gO44fOdmr4aqiSrKGfK72iX+6qqu/b37ucUUVZUpzuA
jnoz1ELXUl35f4iW+yC7orIe3jv7n4BiZj/1iHlnqyStFYZlGXFXCb2ieBxHyso5
HbHEKa4vcJ0uZR26pIkweOTi1IiB5gJ6Dlw7alDTtl2bIzZ3JJ8sg2m9Y93ewWld
8Qapg19tP/ULOv2PpN1PEo62Gyb9WBzue5yTamXsfxupHbsS7nAoi4MVxFwQZYBV
/+HiZRYtDYSbn43A/Wo2gS908pRrNWRFBfDd/bzztAiotPJpMYamHDc7w08SEaM4
J4tujBIdC8KsewwDwnUvMUev8TXxDMbdjijZcDuaPaUOhPoppiaWbRTtoXKbxUam
SdTWuT3dXROLgkgBVD2wyLKWoRKD4SIW1n1gwD6MUr1wnJsss0u2iqSDZvaW9Nsu
K1UhAMCbbCG1WSoENaN2tuj+A0Hpk7mEoFZ9yKopipCsMfOouizG5kKWfu4vwkn2
xVCNyW6oAwKtM5fPo+Vh7U62t2AEHlQePaGfzWek+B0ptoVLFa6v4UPxrVXO2CTg
lseyJH6z2+UV9F0vCWIV/PkaAaVaCeP0q7qTG0E1Wqo7SuGq+q0AOessDeEgfO7m
91L5j36PkAKLvjpwgHl4AQ==
-----END ENCRYPTED PRIVATE KEY-----

13326
React/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

37
React/package.json Normal file
View File

@ -0,0 +1,37 @@
{
"name": "oidcclient",
"version": "0.1.0",
"private": true,
"dependencies": {
"axios": "^0.19.0",
"milligram": "^1.3.0",
"oidc-client": "^1.9.1",
"react": "^16.10.0",
"react-dom": "^16.10.0",
"react-redux": "^7.1.1",
"react-router-dom": "^5.1.0",
"react-scripts": "3.1.2",
"redux": "^4.0.4"
},
"scripts": {
"start": "set HTTPS=true; set SSL_CRT_FILE=cert.pem; set SSL_KEY_FILE=key.pem; react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": "react-app"
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}

BIN
React/public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

26
React/public/index.html Normal file
View File

@ -0,0 +1,26 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<title>We Want Doughnuts</title>
<style>
body {
text-align: center;
}
code {
text-align: left;
}
</style>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
</body>
</html>

36
React/src/App.js Normal file
View File

@ -0,0 +1,36 @@
import React, { useEffect } from 'react';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom'
import SigninOidc from './pages/signin-oidc'
import SignoutOidc from './pages/signout-oidc'
import Home from './pages/home'
import Login from './pages/login'
import { Provider } from 'react-redux';
import store from './store';
import userManager, { loadUserFromStorage } from './services/userService'
import AuthProvider from './utils/authProvider'
import PrivateRoute from './utils/protectedRoute'
function App() {
useEffect(() => {
// fetch current user from cookies
loadUserFromStorage(store)
}, [])
return (
<Provider store={store}>
<AuthProvider userManager={userManager} store={store}>
<Router>
<Switch>
<Route path="/login" component={Login} />
<Route path="/signout-oidc" component={SignoutOidc} />
<Route path="/signin-oidc" component={SigninOidc} />
<PrivateRoute exact path="/" component={Home} />
</Switch>
</Router>
</AuthProvider>
</Provider>
);
}
export default App;

View File

@ -0,0 +1,41 @@
import {
STORE_USER,
USER_SIGNED_OUT,
USER_EXPIRED,
STORE_USER_ERROR,
LOADING_USER
} from './types'
import { setAuthHeader } from '../utils/axiosHeaders'
export function storeUser(user) {
setAuthHeader(user.access_token)
return {
type: STORE_USER,
payload: user
}
}
export function loadingUser() {
return {
type: LOADING_USER
}
}
export function storeUserError() {
return {
type: STORE_USER_ERROR
}
}
export function userExpired() {
return {
type: USER_EXPIRED
}
}
export function userSignedOut() {
return {
type: USER_SIGNED_OUT
}
}

View File

@ -0,0 +1,5 @@
export const STORE_USER = 'STORE_USER'
export const USER_SIGNED_OUT = 'USER_SIGNED_OUT'
export const USER_EXPIRED = 'USER_EXPIRED'
export const STORE_USER_ERROR = 'STORE_USER_ERROR'
export const LOADING_USER = 'LOADING_USER'

6
React/src/index.js Normal file
View File

@ -0,0 +1,6 @@
import React from 'react';
import ReactDOM from 'react-dom';
import 'milligram/dist/milligram.min.css'
import App from './App';
ReactDOM.render(<App />, document.getElementById('root'));

98
React/src/pages/home.js Normal file
View File

@ -0,0 +1,98 @@
import React, { useState } from 'react'
import { signoutRedirect } from '../services/userService'
import { useSelector } from 'react-redux'
import * as apiService from '../services/apiService'
import { prettifyJson } from '../utils/jsonUtils'
function Home() {
const user = useSelector(state => state.auth.user)
const [model, setModel] = useState('')
const [licensePlate, setlicensePlate] = useState('')
const [color, setColor] = useState('White')
const [type, setType] = useState(1)
const [vehicleData, setVehicleData] = useState(null)
function signOut() {
signoutRedirect()
}
async function getVehicles() {
console.log(user);
const vehicles = await apiService.getVehiclesFromApi(user.access_token);
setVehicleData(vehicles);
}
async function registerVehicle() {
await apiService.registerVehicle({ model, licensePlate, color, type }, user.access_token);
await getVehicles();
}
function getType(type) {
switch (type){
case 1:
return "Sedan";
case 2:
return "SUV";
case 3:
return "Pickup";
}
}
// async function getDoughnuts() {
// console.log(user);
// const doughnuts = await apiService.getDoughnutsFromApi(user.id);
// setDoughnutData(doughnuts)
// }
return (
<div>
<h1>Traffic Police</h1>
<p>Hello, {user.profile.given_name}.</p>
<button className="button button-outline" onClick={() => getVehicles()}>Get Vehicles</button>
<button className="button button-clear" onClick={() => signOut()}>Sign Out</button>
<h3>Register Vehicle:</h3>
<form onSubmit={e => e.preventDefault()}>
<div className="form-group">
<label>Model: </label>
<input value={model} type="text" name="model" onChange={e => setModel(e.target.value)} />
<label>License Plate: </label>
<input value={licensePlate} type="text" name="licensePlate" onChange={e => setlicensePlate(e.target.value)} />
<label>Color: </label>
<input value={color} type="text" name="color" onChange={e => setColor(e.target.value)} />
<label>Type: </label>
<select value={type} name="type" onChange={e => setType(e.target.value)} >
<option value="1">Sedan</option>
<option value="2">SUV</option>
<option value="3">Pickup</option>
</select>
</div>
<button className="btn" onClick={() => registerVehicle()} >Register</button>
</form>
<h3>Your Vehicles:</h3>
{
vehicleData ?
<ul>
{ vehicleData.map(v => (<li>{v.color} {v.model} ({v.licensePlate}) - {getType(v.type)}</li>)) }
</ul>
:
<p>No vehicles yet :(</p>
}
<pre>
<code>
{prettifyJson(vehicleData ? vehicleData : 'No vehicles yet :(')}
</code>
</pre>
<p><a target='_blank' rel='noopener noreferrer' href='https://github.com/tappyy/react-IS4-auth-demo'>Github Repo</a></p>
</div>
)
}
export default Home

32
React/src/pages/login.js Normal file
View File

@ -0,0 +1,32 @@
import React from 'react'
import { signinRedirect } from '../services/userService'
import { Redirect } from 'react-router-dom'
import { useSelector } from 'react-redux'
function Login() {
const user = useSelector(state => state.auth.user)
function login() {
signinRedirect()
}
return (
(user) ?
(<Redirect to={'/'} />)
:
(
<div>
<h1>Hello!</h1>
<p>Welcome to We Want Doughnuts.</p>
<p>A demo of using React and Identity Server 4 to authenticate a user via OpenID Connect to gain access to a web API (and some lovely doughnuts).</p>
<p>Start by signing in.</p>
<p>💡 <strong>Tip: </strong><em>User: 'spiderman', Pass: 'spiderman'</em></p>
<button onClick={() => login()}>Login</button>
<p><a target='_blank' rel='noopener noreferrer' href='https://github.com/tappyy/react-IS4-auth-demo'>Github Repo</a></p>
</div>
)
)
}
export default Login

View File

@ -0,0 +1,22 @@
import React, { useEffect } from 'react'
import { signinRedirectCallback } from '../services/userService'
import { useHistory } from 'react-router-dom'
function SigninOidc() {
const history = useHistory()
useEffect(() => {
async function signinAsync() {
await signinRedirectCallback()
history.push('/')
}
signinAsync()
}, [history])
return (
<div>
Redirecting...
</div>
)
}
export default SigninOidc

View File

@ -0,0 +1,22 @@
import React, { useEffect } from 'react'
import { signoutRedirectCallback } from '../services/userService'
import { useHistory } from 'react-router-dom'
function SignoutOidc() {
const history = useHistory()
useEffect(() => {
async function signoutAsync() {
await signoutRedirectCallback()
history.push('/')
}
signoutAsync()
}, [history])
return (
<div>
Redirecting...
</div>
)
}
export default SignoutOidc

View File

@ -0,0 +1,38 @@
import {
USER_SIGNED_OUT,
STORE_USER_ERROR,
USER_EXPIRED,
STORE_USER,
LOADING_USER
} from '../actions/types'
const initialState = {
user: null,
isLoadingUser: false
};
export default function (state = initialState, action) {
switch (action.type) {
case STORE_USER:
return {
...state,
isLoadingUser: false,
user: action.payload
}
case LOADING_USER:
return {
...state,
isLoadingUser: true
}
case USER_EXPIRED:
case STORE_USER_ERROR:
case USER_SIGNED_OUT:
return {
...state,
user: null,
isLoadingUser: false
}
default:
return state
}
}

View File

@ -0,0 +1,6 @@
import { combineReducers } from 'redux';
import authReducer from './authReducer';
export default combineReducers({
auth: authReducer
})

View File

@ -0,0 +1,17 @@
import axios from 'axios'
async function getVehiclesFromApi(access_token) {
const response = await axios.get(`https://localhost:5001/api/Vehicles`, { headers: { 'Authorization': `Bearer ${access_token}` } });
return response.data;
}
async function registerVehicle(vehicle, access_token) {
console.log(vehicle);
const response = await axios.post(`https://localhost:5001/api/Vehicles`, vehicle, { headers: { 'Authorization': `Bearer ${access_token}` } });
return response.data;
}
export {
getVehiclesFromApi,
registerVehicle
}

View File

@ -0,0 +1,46 @@
import { UserManager } from 'oidc-client';
import { storeUserError, storeUser } from '../actions/authActions'
const config = {
authority: "https://localhost:5003",
client_id: "f42aec810e6a4980827037b6ffa7f968",
redirect_uri: "https://localhost:3000/signin-oidc",
response_type: "code",
scope: "openid profile traffic-police-api",
post_logout_redirect_uri: "https://localhost:3000/signout-oidc",
};
const userManager = new UserManager(config)
export async function loadUserFromStorage(store) {
try {
let user = await userManager.getUser()
if (!user) { return store.dispatch(storeUserError()) }
store.dispatch(storeUser(user))
} catch (e) {
console.error(`User not found: ${e}`)
store.dispatch(storeUserError())
}
}
export function signinRedirect() {
return userManager.signinRedirect()
}
export function signinRedirectCallback() {
return userManager.signinRedirectCallback()
}
export function signoutRedirect() {
userManager.clearStaleState()
userManager.removeUser()
return userManager.signoutRedirect()
}
export function signoutRedirectCallback() {
userManager.clearStaleState()
userManager.removeUser()
return userManager.signoutRedirectCallback()
}
export default userManager

13
React/src/store.js Normal file
View File

@ -0,0 +1,13 @@
import { createStore, compose } from 'redux';
import rootReducer from './reducers'
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const initialState = {}
const store = createStore(
rootReducer,
initialState,
composeEnhancers()
)
export default store;

View File

@ -0,0 +1,54 @@
import React, { useEffect, useRef } from 'react';
import { storeUser } from '../actions/authActions'
import { setAuthHeader } from './axiosHeaders';
export default function AuthProvider({ userManager: manager, store, children }) {
let userManager = useRef();
useEffect(() => {
userManager.current = manager
const onUserLoaded = (user) => {
console.log(`user loaded: ${user}`)
store.dispatch(storeUser(user))
}
const onUserUnloaded = () => {
setAuthHeader(null)
console.log(`user unloaded`)
}
const onAccessTokenExpiring = () => {
console.log(`user token expiring`)
}
const onAccessTokenExpired = () => {
console.log(`user token expired`)
}
const onUserSignedOut = () => {
console.log(`user signed out`)
}
// events for user
userManager.current.events.addUserLoaded(onUserLoaded)
userManager.current.events.addUserUnloaded(onUserUnloaded)
userManager.current.events.addAccessTokenExpiring(onAccessTokenExpiring)
userManager.current.events.addAccessTokenExpired(onAccessTokenExpired)
userManager.current.events.addUserSignedOut(onUserSignedOut)
// Specify how to clean up after this effect:
return function cleanup() {
userManager.current.events.removeUserLoaded(onUserLoaded);
userManager.current.events.removeUserUnloaded(onUserUnloaded);
userManager.current.events.removeAccessTokenExpiring(onAccessTokenExpiring)
userManager.current.events.removeAccessTokenExpired(onAccessTokenExpired)
userManager.current.events.removeUserSignedOut(onUserSignedOut)
};
}, [manager, store]);
return (
React.Children.only(children)
)
}

View File

@ -0,0 +1,5 @@
import axios from 'axios'
export function setAuthHeader(token) {
axios.defaults.headers.common['Authorization'] = token ? 'Bearer ' + token : ''
}

View File

@ -0,0 +1,3 @@
export const prettifyJson = (value) => {
return JSON.stringify(value, null, 2);
}

View File

@ -0,0 +1,13 @@
import React from 'react'
import { Route, Redirect } from 'react-router-dom'
import { useSelector } from 'react-redux'
function ProtectedRoute({ children, component: Component, ...rest }) {
const user = useSelector(state => state.auth.user)
return user
? (<Route {...rest} component={Component} />)
: (<Redirect to={'/login'} />)
}
export default ProtectedRoute

10782
React/yarn.lock Normal file

File diff suppressed because it is too large Load Diff