├── .gitignore ├── LICENSE ├── MvcCodeFlowClientManual ├── .nuget │ ├── NuGet.Config │ ├── NuGet.exe │ └── NuGet.targets ├── App_Start │ ├── BundleConfig.cs │ ├── FilterConfig.cs │ └── RouteConfig.cs ├── Content │ ├── Site.css │ ├── bootstrap-grid.css │ ├── bootstrap-grid.css.map │ ├── bootstrap-grid.min.css │ ├── bootstrap-grid.min.css.map │ ├── bootstrap-reboot.css │ ├── bootstrap-reboot.css.map │ ├── bootstrap-reboot.min.css │ ├── bootstrap-reboot.min.css.map │ ├── bootstrap.css │ ├── bootstrap.css.map │ ├── bootstrap.min.css │ └── bootstrap.min.css.map ├── Controllers │ ├── AppController.cs │ ├── CallbackController.cs │ ├── ErrorController.cs │ └── HomeController.cs ├── Global.asax ├── Global.asax.cs ├── MVC Manual Code Flow Client.csproj ├── MVC Manual Code Flow Client.csproj.user ├── MVC Manual Code Flow Client.sln ├── Properties │ └── AssemblyInfo.cs ├── Scripts │ ├── README.md │ ├── _references.js │ ├── bootstrap.bundle.js │ ├── bootstrap.bundle.js.map │ ├── bootstrap.bundle.min.js │ ├── bootstrap.bundle.min.js.map │ ├── bootstrap.js │ ├── bootstrap.js.map │ ├── bootstrap.min.js │ ├── bootstrap.min.js.map │ ├── esm │ │ ├── popper-utils.js │ │ ├── popper-utils.js.map │ │ ├── popper-utils.min.js │ │ ├── popper-utils.min.js.map │ │ ├── popper.js │ │ ├── popper.js.map │ │ ├── popper.min.js │ │ └── popper.min.js.map │ ├── index.d.ts │ ├── jquery-2.1.1.intellisense.js │ ├── jquery-3.3.1.intellisense.js │ ├── jquery-3.3.1.js │ ├── jquery-3.3.1.min.js │ ├── jquery-3.3.1.min.map │ ├── jquery-3.3.1.slim.js │ ├── jquery-3.3.1.slim.min.js │ ├── jquery-3.3.1.slim.min.map │ ├── modernizr-2.8.3.js │ ├── popper-utils.js │ ├── popper-utils.js.map │ ├── popper-utils.min.js │ ├── popper-utils.min.js.map │ ├── popper.js │ ├── popper.js.map │ ├── popper.min.js │ ├── popper.min.js.map │ ├── respond.js │ ├── respond.matchmedia.addListener.js │ ├── respond.matchmedia.addListener.min.js │ ├── respond.min.js │ └── umd │ │ ├── popper-utils.js │ │ ├── popper-utils.js.map │ │ ├── popper-utils.min.js │ │ ├── popper-utils.min.js.map │ │ ├── popper.js │ │ ├── popper.js.map │ │ ├── popper.min.js │ │ └── popper.min.js.map ├── Startup.cs ├── Views │ ├── App │ │ ├── CallService.cshtml │ │ └── Index.cshtml │ ├── Callback │ │ ├── Index.cshtml │ │ └── Token.cshtml │ ├── Error │ │ └── Index.cshtml │ ├── Home │ │ └── Index.cshtml │ ├── Shared │ │ ├── Error.cshtml │ │ └── _Layout.cshtml │ ├── Web.config │ └── _ViewStart.cshtml ├── Web.Debug.config ├── Web.Release.config ├── Web.config ├── favicon.ico └── packages.config ├── README.md └── views ├── Callout.png ├── Ratesample.png ├── Thumbdown.png └── Thumbup.png /.gitignore: -------------------------------------------------------------------------------- 1 | # Build Folders (you can keep bin if you'd like, to store dlls and pdbs) 2 | [Bb]in/ 3 | [Oo]bj/ 4 | SDK Assemblies/ 5 | 6 | # mstest test results 7 | TestResults 8 | 9 | ## Ignore Visual Studio temporary files, build results, and 10 | ## files generated by popular Visual Studio add-ons. 11 | 12 | # User-specific files 13 | *.suo 14 | *.user 15 | *.sln.docstates 16 | 17 | # Build results 18 | [Dd]ebug/ 19 | [Rr]elease/ 20 | x64/ 21 | *_i.c 22 | *_p.c 23 | *.ilk 24 | *.meta 25 | *.obj 26 | *.pch 27 | *.pdb 28 | *.pgc 29 | *.pgd 30 | *.rsp 31 | *.sbr 32 | *.tlb 33 | *.tli 34 | *.tlh 35 | *.tmp 36 | *.log 37 | *.vspscc 38 | *.vssscc 39 | .builds 40 | 41 | # Visual C++ cache files 42 | ipch/ 43 | *.aps 44 | *.ncb 45 | *.opensdf 46 | *.sdf 47 | 48 | # Visual Studio profiler 49 | *.psess 50 | *.vsp 51 | *.vspx 52 | 53 | # Guidance Automation Toolkit 54 | *.gpState 55 | 56 | # ReSharper is a .NET coding add-in 57 | _ReSharper* 58 | 59 | # NCrunch 60 | *.ncrunch* 61 | .*crunch*.local.xml 62 | 63 | # Installshield output folder 64 | [Ee]xpress 65 | 66 | # DocProject is a documentation generator add-in 67 | DocProject/buildhelp/ 68 | DocProject/Help/*.HxT 69 | DocProject/Help/*.HxC 70 | DocProject/Help/*.hhc 71 | DocProject/Help/*.hhk 72 | DocProject/Help/*.hhp 73 | DocProject/Help/Html2 74 | DocProject/Help/html 75 | 76 | # Click-Once directory 77 | publish 78 | 79 | # Publish Web Output 80 | *.Publish.xml 81 | 82 | # NuGet Packages Directory 83 | packages 84 | 85 | # Nuget package creation 86 | NuGet/lib 87 | 88 | # Windows Azure Build Output 89 | csx 90 | *.build.csdef 91 | 92 | # Windows Store app package directory 93 | AppPackages/ 94 | 95 | # Others 96 | [Bb]in 97 | [Oo]bj 98 | sql 99 | TestResults 100 | [Tt]est[Rr]esult* 101 | *.Cache 102 | ClientBin 103 | [Ss]tyle[Cc]op.* 104 | ~$* 105 | *.dbmdl 106 | Generated_Code #added for RIA/Silverlight projects 107 | 108 | # Backup & report files from converting an old project file to a newer 109 | # Visual Studio version. Backup files are not needed, because we have git ;-) 110 | _UpgradeReport_Files/ 111 | Backup*/ 112 | UpgradeLog*.XML 113 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /MvcCodeFlowClientManual/.nuget/NuGet.Config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /MvcCodeFlowClientManual/.nuget/NuGet.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IntuitDeveloper/Oauth2-MVC5-DotnetSampleApp/c570bc4ea6d18d4bca89e468d5aa02a930142559/MvcCodeFlowClientManual/.nuget/NuGet.exe -------------------------------------------------------------------------------- /MvcCodeFlowClientManual/.nuget/NuGet.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | $(MSBuildProjectDirectory)\..\ 5 | 6 | 7 | false 8 | 9 | 10 | false 11 | 12 | 13 | true 14 | 15 | 16 | false 17 | 18 | 19 | 20 | 21 | 22 | 26 | 27 | 28 | 29 | 30 | $([System.IO.Path]::Combine($(SolutionDir), ".nuget")) 31 | 32 | 33 | 34 | 35 | $(SolutionDir).nuget 36 | 37 | 38 | 39 | $(MSBuildProjectDirectory)\packages.$(MSBuildProjectName.Replace(' ', '_')).config 40 | $(MSBuildProjectDirectory)\packages.$(MSBuildProjectName).config 41 | 42 | 43 | 44 | $(MSBuildProjectDirectory)\packages.config 45 | $(PackagesProjectConfig) 46 | 47 | 48 | 49 | 50 | $(NuGetToolsPath)\NuGet.exe 51 | @(PackageSource) 52 | 53 | "$(NuGetExePath)" 54 | mono --runtime=v4.0.30319 "$(NuGetExePath)" 55 | 56 | $(TargetDir.Trim('\\')) 57 | 58 | -RequireConsent 59 | -NonInteractive 60 | 61 | "$(SolutionDir) " 62 | "$(SolutionDir)" 63 | 64 | 65 | $(NuGetCommand) install "$(PackagesConfig)" -source "$(PackageSources)" $(NonInteractiveSwitch) $(RequireConsentSwitch) -solutionDir $(PaddedSolutionDir) 66 | $(NuGetCommand) pack "$(ProjectPath)" -Properties "Configuration=$(Configuration);Platform=$(Platform)" $(NonInteractiveSwitch) -OutputDirectory "$(PackageOutputDir)" -symbols 67 | 68 | 69 | 70 | RestorePackages; 71 | $(BuildDependsOn); 72 | 73 | 74 | 75 | 76 | $(BuildDependsOn); 77 | BuildPackage; 78 | 79 | 80 | 81 | 82 | 83 | 84 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 99 | 100 | 103 | 104 | 105 | 106 | 108 | 109 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 141 | 142 | 143 | 144 | 145 | -------------------------------------------------------------------------------- /MvcCodeFlowClientManual/App_Start/BundleConfig.cs: -------------------------------------------------------------------------------- 1 | using System.Web.Optimization;//Microsoft.AspNet.Web.Optimization 2 | 3 | namespace MvcCodeFlowClientManual 4 | { 5 | public class BundleConfig 6 | { 7 | // For more information on bundling, visit http://go.microsoft.com/fwlink/?LinkId=301862 8 | public static void RegisterBundles(BundleCollection bundles) 9 | { 10 | bundles.Add(new ScriptBundle("~/bundles/jquery").Include( 11 | "~/Scripts/jquery-{version}.js")); 12 | 13 | // Use the development version of Modernizr to develop with and learn from. Then, when you're 14 | // ready for production, use the build tool at http://modernizr.com to pick only the tests you need. 15 | bundles.Add(new ScriptBundle("~/bundles/modernizr").Include( 16 | "~/Scripts/modernizr-*")); 17 | 18 | bundles.Add(new ScriptBundle("~/bundles/bootstrap").Include( 19 | "~/Scripts/bootstrap.js", 20 | "~/Scripts/respond.js")); 21 | 22 | bundles.Add(new StyleBundle("~/Content/css").Include( 23 | "~/Content/bootstrap.css", 24 | "~/Content/site.css")); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /MvcCodeFlowClientManual/App_Start/FilterConfig.cs: -------------------------------------------------------------------------------- 1 | using System.Web.Mvc; 2 | 3 | namespace MvcCodeFlowClientManual 4 | { 5 | public class FilterConfig 6 | { 7 | public static void RegisterGlobalFilters(GlobalFilterCollection filters) 8 | { 9 | filters.Add(new HandleErrorAttribute()); 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /MvcCodeFlowClientManual/App_Start/RouteConfig.cs: -------------------------------------------------------------------------------- 1 | using System.Web.Mvc; 2 | using System.Web.Routing; 3 | 4 | namespace MvcCodeFlowClientManual 5 | { 6 | public class RouteConfig 7 | { 8 | public static void RegisterRoutes(RouteCollection routes) 9 | { 10 | routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); 11 | 12 | routes.MapRoute( 13 | name: "Default", 14 | url: "{controller}/{action}/{id}", 15 | defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional } 16 | ); 17 | //routes.MapRoute( 18 | // name: "ErrorHandler", 19 | // url: "{controller}/{action}/{id}", 20 | // defaults: new { controller = "App", action = "Unauthorised", id = UrlParameter.Optional } 21 | //); 22 | 23 | routes.MapRoute( 24 | "ErrorHandler", 25 | "Error/{action}/{errMsg}", 26 | new { controller = "Error", action = "Index", errMsg = UrlParameter.Optional } 27 | ); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /MvcCodeFlowClientManual/Content/Site.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding-top: 50px; 3 | padding-bottom: 20px; 4 | } 5 | 6 | /* Set padding to keep content from hitting the edges */ 7 | .body-content { 8 | padding-left: 15px; 9 | padding-right: 15px; 10 | } 11 | 12 | /* Set width on the form input elements since they're 100% wide by default */ 13 | input, 14 | select, 15 | textarea { 16 | max-width: 280px; 17 | } 18 | 19 | /* styles for validation helpers */ 20 | .field-validation-error { 21 | color: #b94a48; 22 | } 23 | 24 | .field-validation-valid { 25 | display: none; 26 | } 27 | 28 | input.input-validation-error { 29 | border: 1px solid #b94a48; 30 | } 31 | 32 | input[type="checkbox"].input-validation-error { 33 | border: 0 none; 34 | } 35 | 36 | .validation-summary-errors { 37 | color: #b94a48; 38 | } 39 | 40 | .validation-summary-valid { 41 | display: none; 42 | } -------------------------------------------------------------------------------- /MvcCodeFlowClientManual/Content/bootstrap-reboot.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap Reboot v4.3.1 (https://getbootstrap.com/) 3 | * Copyright 2011-2019 The Bootstrap Authors 4 | * Copyright 2011-2019 Twitter, Inc. 5 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 6 | * Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md) 7 | */ 8 | *, 9 | *::before, 10 | *::after { 11 | box-sizing: border-box; 12 | } 13 | 14 | html { 15 | font-family: sans-serif; 16 | line-height: 1.15; 17 | -webkit-text-size-adjust: 100%; 18 | -webkit-tap-highlight-color: rgba(0, 0, 0, 0); 19 | } 20 | 21 | article, aside, figcaption, figure, footer, header, hgroup, main, nav, section { 22 | display: block; 23 | } 24 | 25 | body { 26 | margin: 0; 27 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; 28 | font-size: 1rem; 29 | font-weight: 400; 30 | line-height: 1.5; 31 | color: #212529; 32 | text-align: left; 33 | background-color: #fff; 34 | } 35 | 36 | [tabindex="-1"]:focus { 37 | outline: 0 !important; 38 | } 39 | 40 | hr { 41 | box-sizing: content-box; 42 | height: 0; 43 | overflow: visible; 44 | } 45 | 46 | h1, h2, h3, h4, h5, h6 { 47 | margin-top: 0; 48 | margin-bottom: 0.5rem; 49 | } 50 | 51 | p { 52 | margin-top: 0; 53 | margin-bottom: 1rem; 54 | } 55 | 56 | abbr[title], 57 | abbr[data-original-title] { 58 | text-decoration: underline; 59 | -webkit-text-decoration: underline dotted; 60 | text-decoration: underline dotted; 61 | cursor: help; 62 | border-bottom: 0; 63 | -webkit-text-decoration-skip-ink: none; 64 | text-decoration-skip-ink: none; 65 | } 66 | 67 | address { 68 | margin-bottom: 1rem; 69 | font-style: normal; 70 | line-height: inherit; 71 | } 72 | 73 | ol, 74 | ul, 75 | dl { 76 | margin-top: 0; 77 | margin-bottom: 1rem; 78 | } 79 | 80 | ol ol, 81 | ul ul, 82 | ol ul, 83 | ul ol { 84 | margin-bottom: 0; 85 | } 86 | 87 | dt { 88 | font-weight: 700; 89 | } 90 | 91 | dd { 92 | margin-bottom: .5rem; 93 | margin-left: 0; 94 | } 95 | 96 | blockquote { 97 | margin: 0 0 1rem; 98 | } 99 | 100 | b, 101 | strong { 102 | font-weight: bolder; 103 | } 104 | 105 | small { 106 | font-size: 80%; 107 | } 108 | 109 | sub, 110 | sup { 111 | position: relative; 112 | font-size: 75%; 113 | line-height: 0; 114 | vertical-align: baseline; 115 | } 116 | 117 | sub { 118 | bottom: -.25em; 119 | } 120 | 121 | sup { 122 | top: -.5em; 123 | } 124 | 125 | a { 126 | color: #007bff; 127 | text-decoration: none; 128 | background-color: transparent; 129 | } 130 | 131 | a:hover { 132 | color: #0056b3; 133 | text-decoration: underline; 134 | } 135 | 136 | a:not([href]):not([tabindex]) { 137 | color: inherit; 138 | text-decoration: none; 139 | } 140 | 141 | a:not([href]):not([tabindex]):hover, a:not([href]):not([tabindex]):focus { 142 | color: inherit; 143 | text-decoration: none; 144 | } 145 | 146 | a:not([href]):not([tabindex]):focus { 147 | outline: 0; 148 | } 149 | 150 | pre, 151 | code, 152 | kbd, 153 | samp { 154 | font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; 155 | font-size: 1em; 156 | } 157 | 158 | pre { 159 | margin-top: 0; 160 | margin-bottom: 1rem; 161 | overflow: auto; 162 | } 163 | 164 | figure { 165 | margin: 0 0 1rem; 166 | } 167 | 168 | img { 169 | vertical-align: middle; 170 | border-style: none; 171 | } 172 | 173 | svg { 174 | overflow: hidden; 175 | vertical-align: middle; 176 | } 177 | 178 | table { 179 | border-collapse: collapse; 180 | } 181 | 182 | caption { 183 | padding-top: 0.75rem; 184 | padding-bottom: 0.75rem; 185 | color: #6c757d; 186 | text-align: left; 187 | caption-side: bottom; 188 | } 189 | 190 | th { 191 | text-align: inherit; 192 | } 193 | 194 | label { 195 | display: inline-block; 196 | margin-bottom: 0.5rem; 197 | } 198 | 199 | button { 200 | border-radius: 0; 201 | } 202 | 203 | button:focus { 204 | outline: 1px dotted; 205 | outline: 5px auto -webkit-focus-ring-color; 206 | } 207 | 208 | input, 209 | button, 210 | select, 211 | optgroup, 212 | textarea { 213 | margin: 0; 214 | font-family: inherit; 215 | font-size: inherit; 216 | line-height: inherit; 217 | } 218 | 219 | button, 220 | input { 221 | overflow: visible; 222 | } 223 | 224 | button, 225 | select { 226 | text-transform: none; 227 | } 228 | 229 | select { 230 | word-wrap: normal; 231 | } 232 | 233 | button, 234 | [type="button"], 235 | [type="reset"], 236 | [type="submit"] { 237 | -webkit-appearance: button; 238 | } 239 | 240 | button:not(:disabled), 241 | [type="button"]:not(:disabled), 242 | [type="reset"]:not(:disabled), 243 | [type="submit"]:not(:disabled) { 244 | cursor: pointer; 245 | } 246 | 247 | button::-moz-focus-inner, 248 | [type="button"]::-moz-focus-inner, 249 | [type="reset"]::-moz-focus-inner, 250 | [type="submit"]::-moz-focus-inner { 251 | padding: 0; 252 | border-style: none; 253 | } 254 | 255 | input[type="radio"], 256 | input[type="checkbox"] { 257 | box-sizing: border-box; 258 | padding: 0; 259 | } 260 | 261 | input[type="date"], 262 | input[type="time"], 263 | input[type="datetime-local"], 264 | input[type="month"] { 265 | -webkit-appearance: listbox; 266 | } 267 | 268 | textarea { 269 | overflow: auto; 270 | resize: vertical; 271 | } 272 | 273 | fieldset { 274 | min-width: 0; 275 | padding: 0; 276 | margin: 0; 277 | border: 0; 278 | } 279 | 280 | legend { 281 | display: block; 282 | width: 100%; 283 | max-width: 100%; 284 | padding: 0; 285 | margin-bottom: .5rem; 286 | font-size: 1.5rem; 287 | line-height: inherit; 288 | color: inherit; 289 | white-space: normal; 290 | } 291 | 292 | progress { 293 | vertical-align: baseline; 294 | } 295 | 296 | [type="number"]::-webkit-inner-spin-button, 297 | [type="number"]::-webkit-outer-spin-button { 298 | height: auto; 299 | } 300 | 301 | [type="search"] { 302 | outline-offset: -2px; 303 | -webkit-appearance: none; 304 | } 305 | 306 | [type="search"]::-webkit-search-decoration { 307 | -webkit-appearance: none; 308 | } 309 | 310 | ::-webkit-file-upload-button { 311 | font: inherit; 312 | -webkit-appearance: button; 313 | } 314 | 315 | output { 316 | display: inline-block; 317 | } 318 | 319 | summary { 320 | display: list-item; 321 | cursor: pointer; 322 | } 323 | 324 | template { 325 | display: none; 326 | } 327 | 328 | [hidden] { 329 | display: none !important; 330 | } 331 | /*# sourceMappingURL=bootstrap-reboot.css.map */ -------------------------------------------------------------------------------- /MvcCodeFlowClientManual/Content/bootstrap-reboot.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap Reboot v4.3.1 (https://getbootstrap.com/) 3 | * Copyright 2011-2019 The Bootstrap Authors 4 | * Copyright 2011-2019 Twitter, Inc. 5 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 6 | * Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md) 7 | */*,::after,::before{box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}article,aside,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}body{margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-size:1rem;font-weight:400;line-height:1.5;color:#212529;text-align:left;background-color:#fff}[tabindex="-1"]:focus{outline:0!important}hr{box-sizing:content-box;height:0;overflow:visible}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem}p{margin-top:0;margin-bottom:1rem}abbr[data-original-title],abbr[title]{text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;border-bottom:0;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#007bff;text-decoration:none;background-color:transparent}a:hover{color:#0056b3;text-decoration:underline}a:not([href]):not([tabindex]){color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus,a:not([href]):not([tabindex]):hover{color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus{outline:0}code,kbd,pre,samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;font-size:1em}pre{margin-top:0;margin-bottom:1rem;overflow:auto}figure{margin:0 0 1rem}img{vertical-align:middle;border-style:none}svg{overflow:hidden;vertical-align:middle}table{border-collapse:collapse}caption{padding-top:.75rem;padding-bottom:.75rem;color:#6c757d;text-align:left;caption-side:bottom}th{text-align:inherit}label{display:inline-block;margin-bottom:.5rem}button{border-radius:0}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,input{overflow:visible}button,select{text-transform:none}select{word-wrap:normal}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{padding:0;border-style:none}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=date],input[type=datetime-local],input[type=month],input[type=time]{-webkit-appearance:listbox}textarea{overflow:auto;resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;max-width:100%;padding:0;margin-bottom:.5rem;font-size:1.5rem;line-height:inherit;color:inherit;white-space:normal}progress{vertical-align:baseline}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:none}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}summary{display:list-item;cursor:pointer}template{display:none}[hidden]{display:none!important} 8 | /*# sourceMappingURL=bootstrap-reboot.min.css.map */ -------------------------------------------------------------------------------- /MvcCodeFlowClientManual/Controllers/AppController.cs: -------------------------------------------------------------------------------- 1 |  2 | using Intuit.Ipp.OAuth2PlatformClient; 3 | using Newtonsoft.Json.Linq; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Net.Http; 8 | using System.Security.Claims; 9 | using System.Threading.Tasks; 10 | using System.Web; 11 | using System.Web.Mvc; 12 | using System.Configuration; 13 | using System.Net; 14 | using System.Net.Http.Headers; 15 | 16 | 17 | namespace MvcCodeFlowClientManual.Controllers 18 | { 19 | 20 | 21 | [Authorize] 22 | public class AppController : Controller 23 | { 24 | public static string mod; 25 | public static string expo; 26 | 27 | public static string clientid = ConfigurationManager.AppSettings["clientid"]; 28 | public static string clientsecret = ConfigurationManager.AppSettings["clientsecret"]; 29 | public static string redirectUrl = ConfigurationManager.AppSettings["redirectUrl"]; 30 | public static string stateCSRFToken = ""; 31 | 32 | public static string authorizeUrl = ""; 33 | public static string tokenEndpoint = ""; 34 | public static string revocationEndpoint = ""; 35 | public static string userinfoEndpoint = ""; 36 | public static string issuerEndpoint = ""; 37 | public static string code = ""; 38 | 39 | public static string access_token = ""; 40 | public static string refresh_token = ""; 41 | public static string identity_token = ""; 42 | public static IList keys; 43 | 44 | 45 | 46 | 47 | 48 | public ActionResult Index() 49 | { 50 | return View(); 51 | } 52 | 53 | 54 | 55 | public async Task CallService() 56 | { 57 | var principal = User as ClaimsPrincipal; 58 | 59 | 60 | string query = "select * from CompanyInfo"; 61 | // build the request 62 | string encodedQuery = WebUtility.UrlEncode(query); 63 | if (Session["realmId"] != null) 64 | { 65 | string realmId = Session["realmId"].ToString(); 66 | 67 | string qboBaseUrl = ConfigurationManager.AppSettings["QBOBaseUrl"]; 68 | 69 | //add qbobase url and query 70 | string uri = string.Format("{0}/v3/company/{1}/query?query={2}", qboBaseUrl, realmId, encodedQuery); 71 | 72 | string result=""; 73 | 74 | try 75 | { 76 | var client = new HttpClient(); 77 | 78 | client.DefaultRequestHeaders.Add("Accept", "application/json;charset=UTF-8"); 79 | client.DefaultRequestHeaders.Add("ContentType", "application/json;charset=UTF-8"); 80 | client.DefaultRequestHeaders.Add("Authorization", "Bearer " + principal.FindFirst("access_token").Value); 81 | 82 | 83 | result = await client.GetStringAsync(uri); 84 | return View("CallService",(object)( "QBO API call success! " + result)); 85 | } 86 | catch (Exception ex) 87 | { 88 | return View("CallService",(object)"QBO API call Failed!"); 89 | } 90 | } 91 | else 92 | return View("CallService",(object)"QBO API call Failed!"); 93 | } 94 | 95 | public async Task RefreshToken() 96 | { 97 | //Refresh Token call 98 | var tokenClient = new TokenClient(AppController.tokenEndpoint, AppController.clientid, AppController.clientsecret); 99 | var principal = User as ClaimsPrincipal; 100 | var refreshToken = principal.FindFirst("refresh_token").Value; 101 | 102 | TokenResponse response = await tokenClient.RequestRefreshTokenAsync(refreshToken); 103 | UpdateCookie(response); 104 | 105 | return RedirectToAction("Index"); 106 | } 107 | 108 | public async Task RevokeAccessToken() 109 | { 110 | var accessToken = (User as ClaimsPrincipal).FindFirst("access_token").Value; 111 | 112 | //Revoke Access token call 113 | var revokeClient = new TokenRevocationClient(AppController.revocationEndpoint, clientid, clientsecret); 114 | 115 | //Revoke access token 116 | TokenRevocationResponse revokeAccessTokenResponse = await revokeClient.RevokeAccessTokenAsync(accessToken); 117 | if (revokeAccessTokenResponse.HttpStatusCode == HttpStatusCode.OK) 118 | { 119 | Session.Abandon(); 120 | Request.GetOwinContext().Authentication.SignOut(); 121 | 122 | }//delete claims and cookies 123 | 124 | return RedirectToAction("Index"); 125 | } 126 | 127 | public async Task RevokeRefreshToken() 128 | { 129 | var refreshToken = (User as ClaimsPrincipal).FindFirst("refresh_token").Value; 130 | 131 | //Revoke Refresh token call 132 | var revokeClient = new TokenRevocationClient(AppController.revocationEndpoint, clientid, clientsecret); 133 | 134 | //Revoke refresh token 135 | TokenRevocationResponse revokeAccessTokenResponse = await revokeClient.RevokeAccessTokenAsync(refreshToken); 136 | if (revokeAccessTokenResponse.HttpStatusCode == HttpStatusCode.OK) 137 | { 138 | Session.Abandon(); 139 | Request.GetOwinContext().Authentication.SignOut(); 140 | } 141 | //return RedirectToAction("Index"); 142 | return RedirectToAction("Index"); 143 | } 144 | 145 | private void UpdateCookie(TokenResponse response) 146 | { 147 | if (response.IsError) 148 | { 149 | throw new Exception(response.Error); 150 | } 151 | 152 | var identity = (User as ClaimsPrincipal).Identities.First(); 153 | var result = from c in identity.Claims 154 | where c.Type != "access_token" && 155 | c.Type != "refresh_token" && 156 | c.Type != "access_token_expires_at" && 157 | c.Type != "access_token_expires_at" 158 | select c; 159 | 160 | var claims = result.ToList(); 161 | 162 | claims.Add(new Claim("access_token", response.AccessToken)); 163 | 164 | claims.Add(new Claim("access_token_expires_at", (DateTime.Now.AddSeconds(response.AccessTokenExpiresIn)).ToString())); 165 | claims.Add(new Claim("refresh_token", response.RefreshToken)); 166 | 167 | claims.Add(new Claim("refresh_token_expires_at", (DateTime.UtcNow.ToEpochTime() + response.RefreshTokenExpiresIn).ToDateTimeFromEpoch().ToString())); 168 | 169 | var newId = new ClaimsIdentity(claims, "Cookies"); 170 | Request.GetOwinContext().Authentication.SignIn(newId); 171 | } 172 | 173 | } 174 | } -------------------------------------------------------------------------------- /MvcCodeFlowClientManual/Controllers/CallbackController.cs: -------------------------------------------------------------------------------- 1 | //using IdentityModel; 2 | //using IdentityModel.Client; 3 | using Intuit.Ipp.OAuth2PlatformClient; 4 | using Newtonsoft.Json; 5 | using Newtonsoft.Json.Linq; 6 | using System; 7 | using System.Collections.Generic; 8 | using System.IdentityModel.Tokens; 9 | using System.Linq; 10 | using System.Net; 11 | using System.Security.Claims; 12 | using System.Security.Cryptography; 13 | using System.Security.Cryptography.X509Certificates; 14 | using System.Text; 15 | using System.Threading.Tasks; 16 | using System.Web; 17 | using System.Web.Mvc; 18 | using System.Configuration; 19 | 20 | namespace MvcCodeFlowClientManual.Controllers 21 | { 22 | public class CallbackController : Controller 23 | { 24 | 25 | 26 | string sub = ""; 27 | string email = ""; 28 | string emailVerified = ""; 29 | string givenName = ""; 30 | string familyName = ""; 31 | string phoneNumber = ""; 32 | string phoneNumberVerified = ""; 33 | string streetAddress = ""; 34 | string locality = ""; 35 | string region = ""; 36 | string postalCode = ""; 37 | string country = ""; 38 | 39 | public static string clientid = ConfigurationManager.AppSettings["clientid"]; 40 | public static string clientsecret = ConfigurationManager.AppSettings["clientsecret"]; 41 | public static string redirectUrl = ConfigurationManager.AppSettings["redirectUrl"]; 42 | 43 | IdTokenJWTClaimTypes payloadData; 44 | 45 | 46 | 47 | 48 | 49 | public async Task Index() 50 | { 51 | ViewBag.Code = Request.QueryString["code"] ?? "none"; 52 | ViewBag.RealmId = Request.QueryString["realmId"] ?? "none"; 53 | 54 | var state = Request.QueryString["state"]; 55 | var tempState = await GetTempStateAsync(); 56 | 57 | if (state.Equals(tempState.Item1, StringComparison.Ordinal)) 58 | { 59 | ViewBag.State = state + " (valid)"; 60 | } 61 | else 62 | { 63 | ViewBag.State = state + " (invalid)"; 64 | } 65 | 66 | ViewBag.Error = Request.QueryString["error"] ?? "none"; 67 | 68 | return View(); 69 | } 70 | 71 | [HttpPost] 72 | [ActionName("Index")] 73 | public async Task Token() 74 | { 75 | //Request Oauth2 tokens 76 | var tokenClient = new TokenClient(AppController.tokenEndpoint, AppController.clientid, AppController.clientsecret); 77 | 78 | 79 | var code = Request.QueryString["code"]; 80 | var realmId = Request.QueryString["realmId"]; 81 | if (realmId != null) 82 | { 83 | Session["realmId"] = realmId; 84 | } 85 | var tempState = await GetTempStateAsync(); 86 | Request.GetOwinContext().Authentication.SignOut("TempState"); 87 | 88 | TokenResponse response = await tokenClient.RequestTokenFromCodeAsync(code, AppController.redirectUrl); 89 | 90 | await ValidateResponseAndSignInAsync(response); 91 | 92 | if (!string.IsNullOrEmpty(response.IdentityToken)) 93 | { 94 | ViewBag.IdentityToken = response.IdentityToken; 95 | } 96 | if (!string.IsNullOrEmpty(response.AccessToken)) 97 | { 98 | ViewBag.AccessToken = response.AccessToken; 99 | } 100 | 101 | return View("Token", response); 102 | } 103 | 104 | private async Task ValidateResponseAndSignInAsync(TokenResponse response) 105 | { 106 | var claims = new List(); 107 | if (!string.IsNullOrWhiteSpace(response.IdentityToken)) 108 | { 109 | 110 | bool isIdTokenValid = ValidateToken(response.IdentityToken);//throw error is not valid 111 | 112 | if (isIdTokenValid == true) 113 | { 114 | claims.Add(new Claim("id_token", response.IdentityToken)); 115 | 116 | } 117 | } 118 | if (Session["realmId"] != null) 119 | { 120 | claims.Add(new Claim("realmId", Session["realmId"].ToString())); 121 | } 122 | 123 | 124 | if (!string.IsNullOrWhiteSpace(response.AccessToken)) 125 | { 126 | var userClaims = await GetUserInfoClaimsAsync(response.AccessToken); 127 | claims.AddRange(userClaims); 128 | 129 | claims.Add(new Claim("access_token", response.AccessToken)); 130 | claims.Add(new Claim("access_token_expires_at", (DateTime.Now.AddSeconds(response.AccessTokenExpiresIn)).ToString())); 131 | } 132 | 133 | if (!string.IsNullOrWhiteSpace(response.RefreshToken)) 134 | { 135 | claims.Add(new Claim("refresh_token", response.RefreshToken)); 136 | claims.Add(new Claim("refresh_token_expires_at", (DateTime.Now.AddSeconds(response.RefreshTokenExpiresIn)).ToString())); 137 | 138 | } 139 | 140 | var id = new ClaimsIdentity(claims, "Cookies"); 141 | Request.GetOwinContext().Authentication.SignIn(id); 142 | 143 | } 144 | 145 | private bool ValidateToken(string identityToken) 146 | { 147 | if (AppController.keys != null) 148 | { 149 | 150 | //IdentityToken 151 | if (identityToken != null) 152 | { 153 | //Split the identityToken to get Header and Payload 154 | string[] splitValues = identityToken.Split('.'); 155 | if (splitValues[0] != null) 156 | { 157 | 158 | //Decode header 159 | var headerJson = Encoding.UTF8.GetString(Base64Url.Decode(splitValues[0].ToString())); 160 | 161 | //Deserilaize headerData 162 | IdTokenHeader headerData = JsonConvert.DeserializeObject(headerJson); 163 | 164 | //Verify if the key id of the key used to sign the payload is not null 165 | if (headerData.Kid == null) 166 | { 167 | return false; 168 | } 169 | 170 | //Verify if the hashing alg used to sign the payload is not null 171 | if (headerData.Alg == null) 172 | { 173 | return false; 174 | } 175 | 176 | } 177 | if (splitValues[1] != null) 178 | { 179 | //Decode payload 180 | var payloadJson = Encoding.UTF8.GetString(Base64Url.Decode(splitValues[1].ToString())); 181 | 182 | 183 | payloadData = JsonConvert.DeserializeObject(payloadJson); 184 | 185 | 186 | 187 | //Verify Aud matches ClientId 188 | if (payloadData.Aud != null) 189 | { 190 | if (payloadData.Aud[0].ToString() != AppController.clientid) 191 | { 192 | return false; 193 | } 194 | } 195 | else 196 | { 197 | return false; 198 | } 199 | 200 | 201 | //Verify Authtime matches the time the ID token was authorized. 202 | if (payloadData.Auth_time == null) 203 | { 204 | return false; 205 | } 206 | 207 | 208 | 209 | //Verify exp matches the time the ID token expires, represented in Unix time (integer seconds). 210 | if (payloadData.Exp != null) 211 | { 212 | long expiration = Convert.ToInt64(payloadData.Exp); 213 | long currentEpochTime = EpochTimeExtensions.ToEpochTime(DateTime.UtcNow); 214 | //Verify the ID expiration time with what expiry time you have calculated and saved in your application 215 | //If they are equal then it means IdToken has expired 216 | 217 | if ((expiration - currentEpochTime) <= 0) 218 | { 219 | return false; 220 | 221 | } 222 | 223 | } 224 | 225 | //Verify Iat matches the time the ID token was issued, represented in Unix time (integer seconds). 226 | if (payloadData.Iat == null) 227 | { 228 | return false; 229 | } 230 | 231 | 232 | //verify Iss matches the issuer identifier for the issuer of the response. 233 | if (payloadData.Iss != null) 234 | { 235 | if (payloadData.Iss.ToString() != AppController.issuerEndpoint) 236 | { 237 | 238 | return false; 239 | } 240 | } 241 | else 242 | { 243 | return false; 244 | } 245 | 246 | 247 | 248 | //Verify sub. Sub is an identifier for the user, unique among all Intuit accounts and never reused. 249 | //An Intuit account can have multiple emails at different points in time, but the sub value is never changed. 250 | //Use sub within your application as the unique-identifier key for the user. 251 | if (payloadData.Sub == null) 252 | { 253 | 254 | return false; 255 | } 256 | 257 | 258 | 259 | } 260 | 261 | //Use external lib to decode mod and expo value and generte hashes 262 | RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(); 263 | 264 | //Read values of n and e from discovery document. 265 | rsa.ImportParameters( 266 | new RSAParameters() 267 | { 268 | //Read values from discovery document 269 | Modulus = Base64Url.Decode(AppController.mod), 270 | Exponent = Base64Url.Decode(AppController.expo) 271 | }); 272 | 273 | //Verify Siganture hash matches the signed concatenation of the encoded header and the encoded payload with the specified algorithm 274 | SHA256 sha256 = SHA256.Create(); 275 | 276 | byte[] hash = sha256.ComputeHash(Encoding.UTF8.GetBytes(splitValues[0] + '.' + splitValues[1])); 277 | 278 | RSAPKCS1SignatureDeformatter rsaDeformatter = new RSAPKCS1SignatureDeformatter(rsa); 279 | rsaDeformatter.SetHashAlgorithm("SHA256"); 280 | if (rsaDeformatter.VerifySignature(hash, Base64Url.Decode(splitValues[2]))) 281 | { 282 | //identityToken is valid 283 | return true; 284 | } 285 | else 286 | { 287 | //identityToken is not valid 288 | return false; 289 | 290 | } 291 | } 292 | else 293 | { 294 | //identityToken is not valid 295 | return false; 296 | } 297 | } 298 | else 299 | { 300 | //Missing mod and expo values 301 | return false; 302 | } 303 | } 304 | 305 | private async Task> GetUserInfoClaimsAsync(string accessToken) 306 | { 307 | IEnumerable userData = new List(); 308 | //Get UserInfo data when correct scope is set for SIWI and Get App now flows 309 | var userInfoClient = new UserInfoClient(AppController.userinfoEndpoint); 310 | 311 | UserInfoResponse userInfoResponse = await userInfoClient.GetAsync(accessToken); 312 | if (userInfoResponse.HttpStatusCode == HttpStatusCode.OK) 313 | { 314 | //Read UserInfo Details 315 | userData = userInfoResponse.Json.ToClaims(); 316 | 317 | foreach (Claim item in userData) 318 | { 319 | if (item.Type == "sub" && item.Value != null) 320 | sub = item.Value; 321 | if (item.Type == "email" && item.Value != null) 322 | email = item.Value; 323 | if (item.Type == "emailVerified" && item.Value != null) 324 | emailVerified = item.Value; 325 | if (item.Type == "givenName" && item.Value != null) 326 | givenName = item.Value; 327 | if (item.Type == "familyName" && item.Value != null) 328 | familyName = item.Value; 329 | if (item.Type == "phoneNumber" && item.Value != null) 330 | phoneNumber = item.Value; 331 | if (item.Type == "phoneNumberVerified" && item.Value != null) 332 | phoneNumberVerified = item.Value; 333 | 334 | if (item.Type == "address" && item.Value != null) 335 | { 336 | 337 | Address jsonObject = JsonConvert.DeserializeObject
(item.Value); 338 | 339 | if (jsonObject.StreetAddress != null) 340 | streetAddress = jsonObject.StreetAddress; 341 | if (jsonObject.Locality != null) 342 | locality = jsonObject.Locality; 343 | if (jsonObject.Region != null) 344 | region = jsonObject.Region; 345 | if (jsonObject.PostalCode != null) 346 | postalCode = jsonObject.PostalCode; 347 | if (jsonObject.Country != null) 348 | country = jsonObject.Country; 349 | } 350 | 351 | } 352 | 353 | } 354 | 355 | return userData; 356 | } 357 | 358 | 359 | 360 | private async Task> GetTempStateAsync() 361 | { 362 | var data = await Request.GetOwinContext().Authentication.AuthenticateAsync("TempState"); 363 | 364 | var state = data.Identity.FindFirst("state").Value; 365 | 366 | 367 | return Tuple.Create(state); 368 | } 369 | } 370 | } -------------------------------------------------------------------------------- /MvcCodeFlowClientManual/Controllers/ErrorController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Web; 5 | using System.Web.Mvc; 6 | 7 | namespace MvcCodeFlowClientManual.Controllers 8 | { 9 | public class ErrorController : Controller 10 | { 11 | // GET: Error 12 | public ActionResult Index() 13 | { 14 | return View(); 15 | } 16 | 17 | } 18 | } -------------------------------------------------------------------------------- /MvcCodeFlowClientManual/Controllers/HomeController.cs: -------------------------------------------------------------------------------- 1 | //using IdentityModel.Client; 2 | using Intuit.Ipp.OAuth2PlatformClient; 3 | using System; 4 | using System.Security.Claims; 5 | using System.Threading.Tasks; 6 | using System.Web; 7 | using System.Web.Mvc; 8 | using System.Configuration; 9 | using System.Net; 10 | using System.Collections.Generic; 11 | 12 | namespace MvcCodeFlowClientManual.Controllers 13 | { 14 | public class HomeController : Controller 15 | { 16 | 17 | 18 | 19 | DiscoveryClient discoveryClient; 20 | DiscoveryResponse doc; 21 | AuthorizeRequest request; 22 | public static IList keys; 23 | public static string scope; 24 | public static string authorizeUrl; 25 | 26 | 27 | public async Task Index() 28 | { 29 | ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12; 30 | 31 | Session.Clear(); 32 | Session.Abandon(); 33 | Request.GetOwinContext().Authentication.SignOut("Cookies"); 34 | 35 | //Intialize DiscoverPolicy 36 | DiscoveryPolicy dpolicy = new DiscoveryPolicy(); 37 | dpolicy.RequireHttps = true; 38 | dpolicy.ValidateIssuerName = true; 39 | 40 | 41 | //Assign the Sandbox Discovery url for the Apps' Dev clientid and clientsecret that you use 42 | //Or 43 | //Assign the Production Discovery url for the Apps' Production clientid and clientsecret that you use 44 | 45 | string discoveryUrl = ConfigurationManager.AppSettings["DiscoveryUrl"]; 46 | 47 | if (discoveryUrl != null && AppController.clientid != null && AppController.clientsecret != null) 48 | { 49 | discoveryClient = new DiscoveryClient(discoveryUrl); 50 | } 51 | else 52 | { 53 | Exception ex= new Exception("Discovery Url missing!"); 54 | throw ex; 55 | } 56 | doc = await discoveryClient.GetAsync(); 57 | 58 | if (doc.StatusCode == HttpStatusCode.OK) 59 | { 60 | //Authorize endpoint 61 | AppController.authorizeUrl = doc.AuthorizeEndpoint; 62 | 63 | //Token endpoint 64 | AppController.tokenEndpoint = doc.TokenEndpoint; 65 | 66 | //Token Revocation enpoint 67 | AppController.revocationEndpoint = doc.RevocationEndpoint; 68 | 69 | //UserInfo endpoint 70 | AppController.userinfoEndpoint = doc.UserInfoEndpoint; 71 | 72 | //Issuer endpoint 73 | AppController.issuerEndpoint = doc.Issuer; 74 | 75 | //JWKS Keys 76 | AppController.keys = doc.KeySet.Keys; 77 | } 78 | 79 | //Get mod and exponent value 80 | foreach (var key in AppController.keys) 81 | { 82 | if (key.N != null) 83 | { 84 | //Mod 85 | AppController.mod = key.N; 86 | } 87 | if (key.E != null) 88 | { 89 | //Exponent 90 | AppController.expo = key.E; 91 | } 92 | } 93 | 94 | 95 | 96 | return View(); 97 | } 98 | 99 | 100 | 101 | public ActionResult MyAction(string submitButton) 102 | { 103 | switch (submitButton) 104 | { 105 | case "C2QB": 106 | // delegate sending to C2QB Action 107 | return (C2QB()); 108 | case "GetAppNow": 109 | // call another action to GetAppNow 110 | return (GetAppNow()); 111 | case "SIWI": 112 | // call another action to SIWI 113 | return (SIWI()); 114 | default: 115 | // If they've submitted the form without a submitButton, 116 | // just return the view again. 117 | return (View()); 118 | } 119 | } 120 | 121 | private ActionResult C2QB() 122 | { 123 | scope = OidcScopes.Accounting.GetStringValue() + " " + OidcScopes.Payment.GetStringValue(); 124 | authorizeUrl = GetAuthorizeUrl(scope); 125 | // perform the redirect here. 126 | return Redirect(authorizeUrl); 127 | } 128 | 129 | private ActionResult GetAppNow() 130 | { 131 | scope = OidcScopes.Accounting.GetStringValue() + " " + OidcScopes.Payment.GetStringValue() + " " + OidcScopes.OpenId.GetStringValue() + " " + OidcScopes.Address.GetStringValue() 132 | + " " + OidcScopes.Email.GetStringValue() + " " + OidcScopes.Phone.GetStringValue() 133 | + " " + OidcScopes.Profile.GetStringValue(); 134 | authorizeUrl = GetAuthorizeUrl(scope); 135 | // perform the redirect here. 136 | return Redirect(authorizeUrl); 137 | } 138 | 139 | private ActionResult SIWI() 140 | { 141 | scope = OidcScopes.OpenId.GetStringValue() + " " + OidcScopes.Address.GetStringValue() 142 | + " " + OidcScopes.Email.GetStringValue() + " " + OidcScopes.Phone.GetStringValue() 143 | + " " + OidcScopes.Profile.GetStringValue(); 144 | authorizeUrl = GetAuthorizeUrl(scope); 145 | // perform the redirect here. 146 | return Redirect(authorizeUrl); 147 | } 148 | 149 | 150 | 151 | 152 | private void SetTempState(string state) 153 | { 154 | var tempId = new ClaimsIdentity("TempState"); 155 | tempId.AddClaim(new Claim("state", state)); 156 | 157 | Request.GetOwinContext().Authentication.SignIn(tempId); 158 | } 159 | 160 | private string GetAuthorizeUrl(string scope) 161 | { 162 | var state = Guid.NewGuid().ToString("N"); 163 | 164 | SetTempState(state); 165 | 166 | //Make Authorization request 167 | var request = new AuthorizeRequest(AppController.authorizeUrl); 168 | 169 | string url = request.CreateAuthorizeUrl( 170 | clientId: AppController.clientid, 171 | responseType: OidcConstants.AuthorizeResponse.Code, 172 | scope: scope, 173 | redirectUri: AppController.redirectUrl, 174 | state: state); 175 | 176 | return url; 177 | } 178 | 179 | 180 | } 181 | } -------------------------------------------------------------------------------- /MvcCodeFlowClientManual/Global.asax: -------------------------------------------------------------------------------- 1 | <%@ Application Codebehind="Global.asax.cs" Inherits="MvcCodeFlowClientManual.MvcApplication" Language="C#" %> 2 | -------------------------------------------------------------------------------- /MvcCodeFlowClientManual/Global.asax.cs: -------------------------------------------------------------------------------- 1 | using System.Web.Mvc; 2 | using System.Web.Optimization; 3 | using System.Web.Routing; 4 | 5 | namespace MvcCodeFlowClientManual 6 | { 7 | public class MvcApplication : System.Web.HttpApplication 8 | { 9 | protected void Application_Start() 10 | { 11 | AreaRegistration.RegisterAllAreas(); 12 | FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); 13 | RouteConfig.RegisterRoutes(RouteTable.Routes); 14 | BundleConfig.RegisterBundles(BundleTable.Bundles); 15 | } 16 | 17 | void Application_EndRequest(object sender, System.EventArgs e) 18 | { 19 | // If the user is not authorised to see this page or access this function, send them to the error page. 20 | if (Response.StatusCode == 401) 21 | { 22 | Response.ClearContent(); 23 | Response.RedirectToRoute("ErrorHandler", (RouteTable.Routes["ErrorHandler"] as Route).Defaults); 24 | } 25 | } 26 | 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /MvcCodeFlowClientManual/MVC Manual Code Flow Client.csproj.user: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | true 5 | 44312 6 | 600 7 | True 8 | False 9 | False 10 | 11 | False 12 | 600 13 | ShowAllFiles 14 | 15 | 16 | 17 | 18 | 19 | Debug|Any CPU 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | CurrentPage 28 | True 29 | False 30 | False 31 | False 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | True 41 | True 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /MvcCodeFlowClientManual/MVC Manual Code Flow Client.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.25420.1 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MVC Manual Code Flow Client", "MVC Manual Code Flow Client.csproj", "{C67DB8BB-6083-436D-9748-4BDD9EC8C0EB}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {C67DB8BB-6083-436D-9748-4BDD9EC8C0EB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {C67DB8BB-6083-436D-9748-4BDD9EC8C0EB}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {C67DB8BB-6083-436D-9748-4BDD9EC8C0EB}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {C67DB8BB-6083-436D-9748-4BDD9EC8C0EB}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | EndGlobal 23 | -------------------------------------------------------------------------------- /MvcCodeFlowClientManual/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | 4 | // General Information about an assembly is controlled through the following 5 | // set of attributes. Change these attribute values to modify the information 6 | // associated with an assembly. 7 | [assembly: AssemblyTitle("MvcCodeFlowClientManual")] 8 | [assembly: AssemblyDescription("")] 9 | [assembly: AssemblyConfiguration("")] 10 | [assembly: AssemblyCompany("")] 11 | [assembly: AssemblyProduct("MvcCodeFlowClientManual")] 12 | [assembly: AssemblyCopyright("Copyright © 2014")] 13 | [assembly: AssemblyTrademark("")] 14 | [assembly: AssemblyCulture("")] 15 | 16 | // Setting ComVisible to false makes the types in this assembly not visible 17 | // to COM components. If you need to access a type in this assembly from 18 | // COM, set the ComVisible attribute to true on that type. 19 | [assembly: ComVisible(false)] 20 | 21 | // The following GUID is for the ID of the typelib if this project is exposed to COM 22 | [assembly: Guid("c955b986-8715-42e1-9f26-c75248466268")] 23 | 24 | // Version information for an assembly consists of the following four values: 25 | // 26 | // Major Version 27 | // Minor Version 28 | // Build Number 29 | // Revision 30 | // 31 | // You can specify all the values or you can default the Revision and Build Numbers 32 | // by using the '*' as shown below: 33 | [assembly: AssemblyVersion("1.0.0.0")] 34 | [assembly: AssemblyFileVersion("1.0.0.0")] 35 | -------------------------------------------------------------------------------- /MvcCodeFlowClientManual/Scripts/README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 |

Popper.js

4 | 5 |

6 | A library used to position poppers in web applications. 7 |

8 | 9 |

10 | Build Status 11 | Stable Release Size 12 | bitHound Overall Score 13 | Istanbul Code Coverage 14 | Get support or discuss 15 |
16 | SauceLabs Reports 17 |

18 | 19 | 20 | 21 | 22 | 23 | ## Wut? Poppers? 24 | 25 | A popper is an element on the screen which "pops out" from the natural flow of your application. 26 | Common examples of poppers are tooltips, popovers and drop-downs. 27 | 28 | 29 | ## So, yet another tooltip library? 30 | 31 | Well, basically, **no**. 32 | Popper.js is a **positioning engine**, its purpose is to calculate the position of an element 33 | to make it possible to position it near a given reference element. 34 | 35 | The engine is completely modular and most of its features are implemented as **modifiers** 36 | (similar to middlewares or plugins). 37 | The whole code base is written in ES2015 and its features are automatically tested on real browsers thanks to [SauceLabs](https://saucelabs.com/) and [TravisCI](https://travis-ci.org/). 38 | 39 | Popper.js has zero dependencies. No jQuery, no LoDash, nothing. 40 | It's used by big companies like [Twitter in Bootstrap v4](https://getbootstrap.com/), [Microsoft in WebClipper](https://github.com/OneNoteDev/WebClipper) and [Atlassian in AtlasKit](https://aui-cdn.atlassian.com/atlaskit/registry/). 41 | 42 | ### Popper.js 43 | 44 | This is the engine, the library that computes and, optionally, applies the styles to 45 | the poppers. 46 | 47 | Some of the key points are: 48 | 49 | - Position elements keeping them in their original DOM context (doesn't mess with your DOM!); 50 | - Allows to export the computed informations to integrate with React and other view libraries; 51 | - Supports Shadow DOM elements; 52 | - Completely customizable thanks to the modifiers based structure; 53 | 54 | Visit our [project page](https://fezvrasta.github.io/popper.js) to see a lot of examples of what you can do with Popper.js! 55 | 56 | Find [the documentation here](/docs/_includes/popper-documentation.md). 57 | 58 | 59 | ### Tooltip.js 60 | 61 | Since lots of users just need a simple way to integrate powerful tooltips in their projects, 62 | we created **Tooltip.js**. 63 | It's a small library that makes it easy to automatically create tooltips using as engine Popper.js. 64 | Its API is almost identical to the famous tooltip system of Bootstrap, in this way it will be 65 | easy to integrate it in your projects. 66 | The tooltips generated by Tooltip.js are accessible thanks to the `aria` tags. 67 | 68 | Find [the documentation here](/docs/_includes/tooltip-documentation.md). 69 | 70 | 71 | ## Installation 72 | Popper.js is available on the following package managers and CDNs: 73 | 74 | | Source | | 75 | |:-------|:---------------------------------------------------------------------------------| 76 | | npm | `npm install popper.js --save` | 77 | | yarn | `yarn add popper.js` | 78 | | NuGet | `PM> Install-Package popper.js` | 79 | | Bower | `bower install popper.js --save` | 80 | | unpkg | [`https://unpkg.com/popper.js`](https://unpkg.com/popper.js) | 81 | | cdnjs | [`https://cdnjs.com/libraries/popper.js`](https://cdnjs.com/libraries/popper.js) | 82 | 83 | Tooltip.js as well: 84 | 85 | | Source | | 86 | |:-------|:---------------------------------------------------------------------------------| 87 | | npm | `npm install tooltip.js --save` | 88 | | yarn | `yarn add tooltip.js` | 89 | | Bower* | `bower install tooltip.js=https://unpkg.com/tooltip.js --save` | 90 | | unpkg | [`https://unpkg.com/tooltip.js`](https://unpkg.com/tooltip.js) | 91 | | cdnjs | [`https://cdnjs.com/libraries/popper.js`](https://cdnjs.com/libraries/popper.js) | 92 | 93 | \*: Bower isn't officially supported, it can be used to install Tooltip.js only trough the unpkg.com CDN. This method has the limitation of not being able to define a specific version of the library. Bower and Popper.js suggests to use npm or Yarn for your projects. 94 | For more info, [read the related issue](https://github.com/FezVrasta/popper.js/issues/390). 95 | 96 | ### Dist targets 97 | 98 | Popper.js is currently shipped with 3 targets in mind: UMD, ESM and ESNext. 99 | 100 | - UMD - Universal Module Definition: AMD, RequireJS and globals; 101 | - ESM - ES Modules: For webpack/Rollup or browser supporting the spec; 102 | - ESNext: Available in `dist/`, can be used with webpack and `babel-preset-env`; 103 | 104 | Make sure to use the right one for your needs. If you want to import it with a `