├── Assets
├── logo.png
├── background.jpg
├── selfAsserted.html
└── global.css
├── CODE_OF_CONDUCT.md
├── LICENSE
├── README.md
├── SECURITY.md
├── AzureFunction
└── AzureFunction.cs
└── .gitignore
/Assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Azure-Samples/active-directory-b2c-dotnet-sign-up-user-flow-captcha/master/Assets/logo.png
--------------------------------------------------------------------------------
/Assets/background.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Azure-Samples/active-directory-b2c-dotnet-sign-up-user-flow-captcha/master/Assets/background.jpg
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Microsoft Open Source Code of Conduct
2 |
3 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
4 |
5 | Resources:
6 |
7 | - [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/)
8 | - [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/)
9 | - Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns
10 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) Microsoft Corporation.
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE
22 |
--------------------------------------------------------------------------------
/Assets/selfAsserted.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Sign in
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
Sign up for a new account
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ---
2 | page_type: sample
3 | languages:
4 | - C#
5 | - .Net core
6 | products:
7 | - azure-active-directory
8 | description: "A sample to demonstrate how to use a captcha service during sign-up in Azure AD B2C user flows"
9 | urlFragment: active-directory-b2c-sign-up-user-flow-captcha
10 | ---
11 |
12 |
13 | # active-directory-b2c-user-flow-captcha
14 |
15 | ## Contents
16 |
17 | | File/folder | Description |
18 | | --------------------------- | ------------------------------------------ |
19 | | [Assets/selfAsserted.html](selfAsserted.html) | Sample custom HTML and JS script file for user flow. |
20 | | [Assets](selfAsserted.html) | Contains UI/UX assets used by the user flows. |
21 | | [AzureFunction.cs](AzureFunction.cs) | Sample source code for HTTP trigger. |
22 | | `README.md` | This README file. |
23 | | `.gitignore` | Define what to ignore at commit time. |
24 | | `LICENSE` | The license for the sample. |
25 |
26 | ## Key Concepts
27 |
28 | Captcha services are often used in authentication scenarios to protect against bots or other automated abuse. This sample demonstrates how to use Azure AD B2C user flows to utilize a captcha service during user sign-up.
29 |
30 | Key components:
31 | - **reCAPTCHA** - a captcha service for protecting against bots and other automated abuse.
32 | - **Azure AD B2C sign-up user flow** - The sign-up experience that will be using the captcha service. Will utilize the **custom page content** and **API connectors** to integrate with the captcha service.
33 | - **Azure Function** - API endpoint hosted by you that works in conjunction with the API connectors feature. This API is responsible for inter-mediating between the user flow and the captcha service to determine whether a user can successfully sign-up.
34 |
35 | This same pattern can be used for other Captcha services and with other API hosting services.
36 |
37 | ## Create a user flow
38 | This can be either be a **sign up and sign in** or a just **sign up** or user flow. [Follow these instructions.](https://docs.microsoft.com/azure/active-directory-b2c/tutorial-create-user-flows).
39 |
40 | ## Create an API key pair for reCAPTCHA V3
41 |
42 | - Follow the [reCAPTCHA documentation](https://developers.google.com/recaptcha/intro) to create an API key pair for your site.
43 | - Use your Azure AD B2C tenant as the **domain**: `.b2clogin.com`
44 | - You will receive a **site_key** and **secret_key**. The values of these are referred to as **`CAPTCHA_SITE_KEY`** and **`CAPTCHA_SECRET_KEY`** in the sample code.
45 |
46 |
47 |
--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | ## Security
4 |
5 | Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/).
6 |
7 | If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://docs.microsoft.com/previous-versions/tn-archive/cc751383(v=technet.10)), please report it to us as described below.
8 |
9 | ## Reporting Security Issues
10 |
11 | **Please do not report security vulnerabilities through public GitHub issues.**
12 |
13 | Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://msrc.microsoft.com/create-report).
14 |
15 | If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://www.microsoft.com/msrc/pgp-key-msrc).
16 |
17 | You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://www.microsoft.com/msrc).
18 |
19 | Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue:
20 |
21 | * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.)
22 | * Full paths of source file(s) related to the manifestation of the issue
23 | * The location of the affected source code (tag/branch/commit or direct URL)
24 | * Any special configuration required to reproduce the issue
25 | * Step-by-step instructions to reproduce the issue
26 | * Proof-of-concept or exploit code (if possible)
27 | * Impact of the issue, including how an attacker might exploit the issue
28 |
29 | This information will help us triage your report more quickly.
30 |
31 | If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://microsoft.com/msrc/bounty) page for more details about our active programs.
32 |
33 | ## Preferred Languages
34 |
35 | We prefer all communications to be in English.
36 |
37 | ## Policy
38 |
39 | Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://www.microsoft.com/msrc/cvd).
40 |
41 |
--------------------------------------------------------------------------------
/AzureFunction/AzureFunction.cs:
--------------------------------------------------------------------------------
1 | #r "Newtonsoft.Json"
2 |
3 | using System.Net;
4 | using Microsoft.AspNetCore.Mvc;
5 | using Newtonsoft.Json;
6 | using System.Text;
7 |
8 | public static async Task Run(HttpRequest req, ILogger log)
9 | {
10 | log.LogInformation("C# HTTP trigger function processed a request.");
11 |
12 | string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
13 | log.LogInformation("Request body: " + requestBody);
14 |
15 | // Check HTTP basic authorization
16 | if (!Authorize(req, log))
17 | {
18 | log.LogWarning("HTTP basic authentication validation failed.");
19 | var response = new HttpResponseMessage(HttpStatusCode.BadRequest) {
20 | Content = new StringContent(JsonConvert.SerializeObject(new ResponseObject() {
21 | action = "ShowBlockPage",
22 | userMessage = "Captcha verification failed due to invalid auth. Please contact the administrator to fix this issue.",
23 | code = "B2C004",
24 | status = 400,
25 | version = "1.0.0"
26 | }), Encoding.UTF8, "application/json")
27 | };
28 | string responseBody = await response.Content.ReadAsStringAsync();
29 | log.LogInformation("Response: " + responseBody);
30 | return response;
31 | }
32 |
33 | dynamic data = JsonConvert.DeserializeObject(requestBody);
34 | string extension_Captchatext = data?.extension_aaebe32dff39461b940c8245c5b7dc33_Captchatext; //extension app-id
35 | bool verified_captcha = !string.IsNullOrEmpty(extension_Captchatext);
36 |
37 | using(var client = new HttpClient())
38 | {
39 | Dictionary dictionary = new Dictionary();
40 | dictionary.Add("secret", System.Environment.GetEnvironmentVariable("SECRET_KEY"));
41 | dictionary.Add("response", extension_Captchatext);
42 | var formContent = new FormUrlEncodedContent(dictionary);
43 |
44 | var result = await client.PostAsync("https://www.google.com/recaptcha/api/siteverify", formContent);
45 | string resultContent = await result.Content.ReadAsStringAsync();
46 | log.LogInformation("Response from captcha service: " + resultContent);
47 | dynamic data_captcha = JsonConvert.DeserializeObject(resultContent);
48 | verified_captcha = data_captcha.success;
49 | }
50 |
51 | if (verified_captcha)
52 | {
53 | var response = new HttpResponseMessage(HttpStatusCode.OK) {
54 | Content = new StringContent(JsonConvert.SerializeObject(new ResponseObjectNoMessage()
55 | {
56 | action = "Continue",
57 | extension_Captchatext = ""
58 | }), Encoding.UTF8, "application/json")
59 | };
60 | string responseBody = await response.Content.ReadAsStringAsync();
61 | log.LogInformation("Response: " + responseBody);
62 | return response;
63 | }
64 | else
65 | {
66 | var response = new HttpResponseMessage(HttpStatusCode.BadRequest) {
67 | Content = new StringContent(JsonConvert.SerializeObject(new ResponseObject() {
68 | action = "ShowBlockPage",
69 | userMessage = "Invalid captcha or captcha expired",
70 | code = "B2C003",
71 | status = 400,
72 | version = "1.0.0"
73 | }), Encoding.UTF8, "application/json")
74 | };
75 | string responseBody = await response.Content.ReadAsStringAsync();
76 | log.LogInformation("Response: " + responseBody);
77 | return response;
78 | }
79 | }
80 |
81 | private static bool Authorize(HttpRequest req, ILogger log)
82 | {
83 | // Get the environment's credentials
84 | string username = System.Environment.GetEnvironmentVariable("BASIC_AUTH_USERNAME", EnvironmentVariableTarget.Process);
85 | string password = System.Environment.GetEnvironmentVariable("BASIC_AUTH_PASSWORD", EnvironmentVariableTarget.Process);
86 |
87 | // Returns authorized if the username is empty or not exists.
88 | if (string.IsNullOrEmpty(username))
89 | {
90 | log.LogInformation("HTTP basic authentication is not set.");
91 | return true;
92 | }
93 |
94 | // Check if the HTTP Authorization header exist
95 | if (!req.Headers.ContainsKey("Authorization"))
96 | {
97 | log.LogWarning("Missing HTTP basic authentication header.");
98 | return false;
99 | }
100 |
101 | // Read the authorization header
102 | var auth = req.Headers["Authorization"].ToString();
103 |
104 | // Ensure the type of the authorization header id `Basic`
105 | if (!auth.StartsWith("Basic "))
106 | {
107 | log.LogWarning("HTTP basic authentication header must start with 'Basic '.");
108 | return false;
109 | }
110 |
111 | // Get the the HTTP basic authorization credentials
112 | var cred = System.Text.UTF8Encoding.UTF8.GetString(Convert.FromBase64String(auth.Substring(6))).Split(':');
113 |
114 | // Evaluate the credentials and return the result
115 | return (cred[0] == username && cred[1] == password) ;
116 | }
117 |
118 | public class ResponseObject
119 | {
120 | public string action { get; set; }
121 | public string userMessage { get; set; }
122 | public string code { get; set; }
123 | public string version { get; set; }
124 | public int status { get; set; }
125 | }
126 |
127 | private class ResponseObjectNoMessage
128 | {
129 | public string action { get; set; }
130 | public string extension_Captchatext { get; set; }
131 | }
132 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 | ##
4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
5 |
6 | # User-specific files
7 | *.rsuser
8 | *.suo
9 | *.user
10 | *.userosscache
11 | *.sln.docstates
12 | *.ds_store
13 |
14 | # User-specific files (MonoDevelop/Xamarin Studio)
15 | *.userprefs
16 |
17 | # Mono auto generated files
18 | mono_crash.*
19 |
20 | # Build results
21 | [Dd]ebug/
22 | [Dd]ebugPublic/
23 | [Rr]elease/
24 | [Rr]eleases/
25 | x64/
26 | x86/
27 | [Aa][Rr][Mm]/
28 | [Aa][Rr][Mm]64/
29 | bld/
30 | [Bb]in/
31 | [Oo]bj/
32 | [Ll]og/
33 | [Ll]ogs/
34 |
35 | # Visual Studio 2015/2017 cache/options directory
36 | .vs/
37 | # Uncomment if you have tasks that create the project's static files in wwwroot
38 | #wwwroot/
39 |
40 | # Visual Studio 2017 auto generated files
41 | Generated\ Files/
42 |
43 | # MSTest test Results
44 | [Tt]est[Rr]esult*/
45 | [Bb]uild[Ll]og.*
46 |
47 | # NUnit
48 | *.VisualState.xml
49 | TestResult.xml
50 | nunit-*.xml
51 |
52 | # Build Results of an ATL Project
53 | [Dd]ebugPS/
54 | [Rr]eleasePS/
55 | dlldata.c
56 |
57 | # Benchmark Results
58 | BenchmarkDotNet.Artifacts/
59 |
60 | # .NET Core
61 | project.lock.json
62 | project.fragment.lock.json
63 | artifacts/
64 |
65 | # StyleCop
66 | StyleCopReport.xml
67 |
68 | # Files built by Visual Studio
69 | *_i.c
70 | *_p.c
71 | *_h.h
72 | *.ilk
73 | *.meta
74 | *.obj
75 | *.iobj
76 | *.pch
77 | *.pdb
78 | *.ipdb
79 | *.pgc
80 | *.pgd
81 | *.rsp
82 | *.sbr
83 | *.tlb
84 | *.tli
85 | *.tlh
86 | *.tmp
87 | *.tmp_proj
88 | *_wpftmp.csproj
89 | *.log
90 | *.vspscc
91 | *.vssscc
92 | .builds
93 | *.pidb
94 | *.svclog
95 | *.scc
96 |
97 | # Chutzpah Test files
98 | _Chutzpah*
99 |
100 | # Visual C++ cache files
101 | ipch/
102 | *.aps
103 | *.ncb
104 | *.opendb
105 | *.opensdf
106 | *.sdf
107 | *.cachefile
108 | *.VC.db
109 | *.VC.VC.opendb
110 |
111 | # Visual Studio profiler
112 | *.psess
113 | *.vsp
114 | *.vspx
115 | *.sap
116 |
117 | # Visual Studio Trace Files
118 | *.e2e
119 |
120 | # TFS 2012 Local Workspace
121 | $tf/
122 |
123 | # Guidance Automation Toolkit
124 | *.gpState
125 |
126 | # ReSharper is a .NET coding add-in
127 | _ReSharper*/
128 | *.[Rr]e[Ss]harper
129 | *.DotSettings.user
130 |
131 | # TeamCity is a build add-in
132 | _TeamCity*
133 |
134 | # DotCover is a Code Coverage Tool
135 | *.dotCover
136 |
137 | # AxoCover is a Code Coverage Tool
138 | .axoCover/*
139 | !.axoCover/settings.json
140 |
141 | # Visual Studio code coverage results
142 | *.coverage
143 | *.coveragexml
144 |
145 | # NCrunch
146 | _NCrunch_*
147 | .*crunch*.local.xml
148 | nCrunchTemp_*
149 |
150 | # MightyMoose
151 | *.mm.*
152 | AutoTest.Net/
153 |
154 | # Web workbench (sass)
155 | .sass-cache/
156 |
157 | # Installshield output folder
158 | [Ee]xpress/
159 |
160 | # DocProject is a documentation generator add-in
161 | DocProject/buildhelp/
162 | DocProject/Help/*.HxT
163 | DocProject/Help/*.HxC
164 | DocProject/Help/*.hhc
165 | DocProject/Help/*.hhk
166 | DocProject/Help/*.hhp
167 | DocProject/Help/Html2
168 | DocProject/Help/html
169 |
170 | # Click-Once directory
171 | publish/
172 |
173 | # Publish Web Output
174 | *.[Pp]ublish.xml
175 | *.azurePubxml
176 | # Note: Comment the next line if you want to checkin your web deploy settings,
177 | # but database connection strings (with potential passwords) will be unencrypted
178 | *.pubxml
179 | *.publishproj
180 |
181 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
182 | # checkin your Azure Web App publish settings, but sensitive information contained
183 | # in these scripts will be unencrypted
184 | PublishScripts/
185 |
186 | # NuGet Packages
187 | *.nupkg
188 | # NuGet Symbol Packages
189 | *.snupkg
190 | # The packages folder can be ignored because of Package Restore
191 | **/[Pp]ackages/*
192 | # except build/, which is used as an MSBuild target.
193 | !**/[Pp]ackages/build/
194 | # Uncomment if necessary however generally it will be regenerated when needed
195 | #!**/[Pp]ackages/repositories.config
196 | # NuGet v3's project.json files produces more ignorable files
197 | *.nuget.props
198 | *.nuget.targets
199 |
200 | # Microsoft Azure Build Output
201 | csx/
202 | *.build.csdef
203 |
204 | # Microsoft Azure Emulator
205 | ecf/
206 | rcf/
207 |
208 | # Windows Store app package directories and files
209 | AppPackages/
210 | BundleArtifacts/
211 | Package.StoreAssociation.xml
212 | _pkginfo.txt
213 | *.appx
214 | *.appxbundle
215 | *.appxupload
216 |
217 | # Visual Studio cache files
218 | # files ending in .cache can be ignored
219 | *.[Cc]ache
220 | # but keep track of directories ending in .cache
221 | !?*.[Cc]ache/
222 |
223 | # Others
224 | ClientBin/
225 | ~$*
226 | *~
227 | *.dbmdl
228 | *.dbproj.schemaview
229 | *.jfm
230 | *.pfx
231 | *.publishsettings
232 | orleans.codegen.cs
233 |
234 | # Including strong name files can present a security risk
235 | # (https://github.com/github/gitignore/pull/2483#issue-259490424)
236 | #*.snk
237 |
238 | # Since there are multiple workflows, uncomment next line to ignore bower_components
239 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
240 | #bower_components/
241 |
242 | # RIA/Silverlight projects
243 | Generated_Code/
244 |
245 | # Backup & report files from converting an old project file
246 | # to a newer Visual Studio version. Backup files are not needed,
247 | # because we have git ;-)
248 | _UpgradeReport_Files/
249 | Backup*/
250 | UpgradeLog*.XML
251 | UpgradeLog*.htm
252 | ServiceFabricBackup/
253 | *.rptproj.bak
254 |
255 | # SQL Server files
256 | *.mdf
257 | *.ldf
258 | *.ndf
259 |
260 | # Business Intelligence projects
261 | *.rdl.data
262 | *.bim.layout
263 | *.bim_*.settings
264 | *.rptproj.rsuser
265 | *- [Bb]ackup.rdl
266 | *- [Bb]ackup ([0-9]).rdl
267 | *- [Bb]ackup ([0-9][0-9]).rdl
268 |
269 | # Microsoft Fakes
270 | FakesAssemblies/
271 |
272 | # GhostDoc plugin setting file
273 | *.GhostDoc.xml
274 |
275 | # Node.js Tools for Visual Studio
276 | .ntvs_analysis.dat
277 | node_modules/
278 |
279 | # Visual Studio 6 build log
280 | *.plg
281 |
282 | # Visual Studio 6 workspace options file
283 | *.opt
284 |
285 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
286 | *.vbw
287 |
288 | # Visual Studio LightSwitch build output
289 | **/*.HTMLClient/GeneratedArtifacts
290 | **/*.DesktopClient/GeneratedArtifacts
291 | **/*.DesktopClient/ModelManifest.xml
292 | **/*.Server/GeneratedArtifacts
293 | **/*.Server/ModelManifest.xml
294 | _Pvt_Extensions
295 |
296 | # Paket dependency manager
297 | .paket/paket.exe
298 | paket-files/
299 |
300 | # FAKE - F# Make
301 | .fake/
302 |
303 | # CodeRush personal settings
304 | .cr/personal
305 |
306 | # Python Tools for Visual Studio (PTVS)
307 | __pycache__/
308 | *.pyc
309 |
310 | # Cake - Uncomment if you are using it
311 | # tools/**
312 | # !tools/packages.config
313 |
314 | # Tabs Studio
315 | *.tss
316 |
317 | # Telerik's JustMock configuration file
318 | *.jmconfig
319 |
320 | # BizTalk build output
321 | *.btp.cs
322 | *.btm.cs
323 | *.odx.cs
324 | *.xsd.cs
325 |
326 | # OpenCover UI analysis results
327 | OpenCover/
328 |
329 | # Azure Stream Analytics local run output
330 | ASALocalRun/
331 |
332 | # MSBuild Binary and Structured Log
333 | *.binlog
334 |
335 | # NVidia Nsight GPU debugger configuration file
336 | *.nvuser
337 |
338 | # MFractors (Xamarin productivity tool) working folder
339 | .mfractor/
340 |
341 | # Local History for Visual Studio
342 | .localhistory/
343 |
344 | # BeatPulse healthcheck temp database
345 | healthchecksdb
346 |
347 | # Backup folder for Package Reference Convert tool in Visual Studio 2017
348 | MigrationBackup/
349 |
350 | # Ionide (cross platform F# VS Code tools) working folder
351 | .ionide/
352 |
353 | # Local settings
354 | local.settings.json
355 |
356 | # Misc
357 | .DS_Store
--------------------------------------------------------------------------------
/Assets/global.css:
--------------------------------------------------------------------------------
1 | @font-face {
2 | font-family: 'Ubuntu-Bold';
3 | src: url(../fonts/Ubuntu-Bold.ttf) format('truetype');
4 | }
5 |
6 | @font-face {
7 | font-family: 'Ubuntu-Regular';
8 | src: url(../fonts/Ubuntu-Regular.ttf) format('truetype');
9 | }
10 |
11 | html {
12 | background: none;
13 | }
14 |
15 | body {
16 | background-attachment: fixed;
17 | background-image: url("../images/background.jpg");
18 | background-position: center;
19 | background-repeat: no-repeat;
20 | background-size: cover;
21 | text-align: left;
22 | }
23 |
24 | @media(max-width:480px) {
25 | body {
26 | background-image: none;
27 | background-color: #f2f2f2;
28 | }
29 |
30 | .divider h2 {
31 | margin: 10px 0 !important;
32 | }
33 |
34 | .login-logo {
35 | height: 80px !important;
36 | margin-top: -65px !important;
37 | }
38 | }
39 |
40 | ::-moz-placeholder {
41 | color: transparent;
42 | }
43 |
44 | ::-moz-placeholder {
45 | color: transparent;
46 | }
47 |
48 | :-ms-input-placeholder {
49 | color: transparent;
50 | }
51 |
52 | ::-webkit-input-placeholder {
53 | color: transparent;
54 | }
55 |
56 | label,
57 | .password-label {
58 | margin-top: 10px;
59 | }
60 |
61 | #AdfsExchange {
62 | background-color: #28B1E6;
63 | background-image: url("../images/activedirectory.png");
64 | background-repeat: no-repeat;
65 | background-size: 50px;
66 | margin: 10px 0;
67 | padding-left: 55px;
68 | }
69 |
70 | #AdfsExchange:hover {
71 | background-color: #189DCF;
72 | }
73 |
74 | #api ul {
75 | list-style-type: none;
76 | padding-left: 0;
77 | }
78 |
79 | #api[data-name='IdpSelections'] ul {
80 | text-align: center;
81 | }
82 |
83 | #api[data-name='Phonefactor'] .buttons button#cancel {
84 | width: 32%;
85 | }
86 |
87 | #api[data-name='SelfAsserted'] > div:first-child {
88 | display: none;
89 | }
90 |
91 | #createAccount{
92 | cursor: pointer;
93 | padding-left:2px;
94 | transition: 0.5s all;
95 | -moz-transition: 0.5s all;
96 | -ms-transition: 0.5s all;
97 | -o-transition: 0.5s all;
98 | -webkit-transition: 0.5s all;
99 | }
100 |
101 | #email_ver_input {
102 | margin-bottom: 10px 0;
103 | }
104 |
105 | #FacebookExchange {
106 | background-color: #3B5595;
107 | background-image: url("../images/facebook.png");
108 | background-repeat: no-repeat;
109 | background-size: 50px;
110 | padding-left: 55px;
111 | }
112 |
113 | #FacebookExchange:hover {
114 | background-color: #354C86
115 | }
116 |
117 | #forgotPassword {
118 | clear: right;
119 | cursor: pointer;
120 | float: right;
121 | margin-top: 10px;
122 | transition: 0.5s all;
123 | -moz-transition: 0.5s all;
124 | -ms-transition: 0.5s all;
125 | -o-transition: 0.5s all;
126 | -webkit-transition: 0.5s all;
127 | }
128 |
129 | #GoogleExchange {
130 | background-color: #C64A29;
131 | background-image: url("../images/googleplus.png");
132 | background-repeat: no-repeat;
133 | background-size: 50px;
134 | margin: 10px 0;
135 | padding-left: 55px;
136 | }
137 |
138 | #GoogleExchange:hover {
139 | background-color: #C14325;
140 | }
141 |
142 | #IdentifyServerExchange {
143 | background-color: #28B1E6;
144 | background-image: url("../images/wingtipcorp.png");
145 | background-repeat: no-repeat;
146 | background-size: 50px;
147 | margin-top: 10px;
148 | padding-left: 55px;
149 | }
150 |
151 | #IdentifyServerExchange:hover {
152 | background-color: #189DCF;
153 | }
154 |
155 | #logonIdentifier,
156 | #password,
157 | #signInName {
158 | background-color: #fff !important;
159 | background-image: none !important;
160 | border: 1px solid #ccc;
161 | border-radius: 2px !important;
162 | box-shadow: inset 0 1px 1px rgba(0,0,0,.075) !important;
163 | color: #555;
164 | display: block;
165 | font-size: 14px;
166 | height: 40px;
167 | line-height: 1.42857143;
168 | margin: 10px 0;
169 | padding: 6px 12px;
170 | transition: border-color ease-in-out .15s,box-shadow ease-in-out .15s !important;
171 | width: 100% !important;
172 | -o-transition: border-color ease-in-out .15s,box-shadow ease-in-out .15s !important;
173 | -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075) !important;
174 | -webkit-transition: border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s !important;
175 | }
176 |
177 | #logo img {
178 | float: left;
179 | height: 50px;
180 | width: 50px;
181 | }
182 |
183 | #MicrosoftAccountExchange {
184 | background-color: #28B1E6;
185 | background-image: url("../images/microsoft.png");
186 | background-repeat: no-repeat;
187 | background-size: 50px;
188 | padding-left: 55px;
189 | }
190 |
191 | #MicrosoftAccountExchange:hover {
192 | background-color: #189DCF;
193 | }
194 |
195 | #SignUpWithLogonEmailExchange {
196 | background-color: #ff5c62;
197 | background-image: url("../images/wingtiptoys.png");
198 | background-repeat: no-repeat;
199 | background-size: 50px;
200 | color: #fff;
201 | margin-top: 10px;
202 | padding-left: 55px;
203 | }
204 |
205 | #SignUpWithLogonEmailExchange:hover{
206 | background-image: url("../images/wingtiptoys.png") !important;
207 | }
208 |
209 | .accountButton {
210 | border: none;
211 | border-radius: 2px;
212 | color: whitesmoke;
213 | font-size: larger;
214 | height: 45px;
215 | width: 284px;
216 | }
217 |
218 | .accountButton:hover {
219 | border: none
220 | }
221 |
222 | .attrEntry,
223 | .phoneEntry {
224 | margin-bottom: 15px;
225 | padding-top: 0;
226 | }
227 |
228 | .attrEntry input,
229 | .attrEntry select,
230 | .phoneEntry input,
231 | .phoneEntry select,
232 | #codeVerification input {
233 | background-color: #fff !important;
234 | background-image: none !important;
235 | border: 1px solid #ccc !important;
236 | border-radius: 2px !important;
237 | box-shadow: inset 0 1px 1px rgba(0,0,0,.075) !important;
238 | color: #555;
239 | display: block;
240 | font-size: 14px;
241 | height: 40px;
242 | line-height: 1.42857143;
243 | padding: 6px 12px;
244 | transition: border-color ease-in-out .15s,box-shadow ease-in-out .15s !important;
245 | width: 100% !important;
246 | -o-transition: border-color ease-in-out .15s,box-shadow ease-in-out .15s !important;
247 | -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075) !important;
248 | -webkit-transition: border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s !important;
249 | }
250 |
251 | .attrEntry input:invalid,
252 | .phoneEntry input:invalid,
253 | #codeVerification input:invalid {
254 | border-color: inherit;
255 | }
256 |
257 | .attrEntry.validate input:invalid,
258 | .phoneEntry.validate input:invalid,
259 | #codeVerification.validate input:invalid {
260 | border-color: #a94442 !important;
261 | box-shadow: inset 0 1px 1px rgba(0,0,0,.075) !important;
262 | -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075) !important;
263 | }
264 |
265 | .attrEntry #email_intro {
266 | display: none !important;
267 | }
268 |
269 | .attrEntry .error.itemLevel,
270 | .attrEntry .helpText,
271 | .phoneEntry .error.itemLevel,
272 | .phoneEntry .helpText,
273 | #codeVerification .error.itemLevel,
274 | #codeVerification .helpText {
275 | display: none;
276 | }
277 |
278 | .attrEntry .tiny,
279 | .phoneEntry .tiny,
280 | #codeVerification .tiny {
281 | display: none;
282 | }
283 |
284 | .buttons button {
285 | background-image: none;
286 | border: 1px solid transparent;
287 | border-radius: 4px;
288 | cursor: pointer;
289 | display: inline-block;
290 | font-size: 14px;
291 | font-weight: 400;
292 | height: inherit;
293 | line-height: 1.42857143;
294 | margin: 0;
295 | padding: 6px 12px;
296 | text-align: center;
297 | touch-action: manipulation;
298 | user-select: none;
299 | vertical-align: middle;
300 | white-space: nowrap;
301 | width: inherit;
302 | -moz-user-select: none;
303 | -ms-touch-action: manipulation;
304 | -ms-user-select: none;
305 | -webkit-user-select: none;
306 | }
307 |
308 | .buttons{
309 | margin: 20px 0;
310 | }
311 | .buttons button#cancel,
312 | .buttons button#email_ver_but_edit,
313 | .buttons button#email_ver_but_resend {
314 | background-color: #fff;
315 | border-color: #ccc;
316 | color: #333;
317 | }
318 |
319 | .buttons button#cancel {
320 | font-size: 18px;
321 | height: 60px;
322 | width: 49.9%;
323 | }
324 |
325 | @media(max-width:767px) {
326 | .buttons button#cancel{
327 | height: 50px;
328 | width: 48.9%;
329 | }
330 | }
331 |
332 | .buttons button:hover#cancel,
333 | .buttons button:hover#email_ver_but_edit,
334 | .buttons button:hover#email_ver_but_resend {
335 | background-color: #e6e6e6;
336 | background-image: none;
337 | border-color: #adadad;
338 | color: #333;
339 | }
340 |
341 | .buttons button#continue {
342 | background-color: #5cb85c;
343 | border-bottom: 5px solid #449d44;
344 | border-color: #4cae4c;
345 | color: #fff;
346 | font-size: 18px;
347 | height: 60px;
348 | width: 49%;
349 | }
350 |
351 | @media(max-width:767px){
352 | .buttons button#continue {
353 | height: 50px;
354 | }
355 | }
356 |
357 | .buttons button:hover#continue {
358 | background-color: #449d44;
359 | background-image: none;
360 | border-color: #398439;
361 | color: #fff;
362 | }
363 |
364 | .buttons button[disabled]#continue,
365 | .buttons button[disabled]:hover#continue {
366 | background-color: #5cb85c;
367 | background-image: none;
368 | border-color: #4cae4c;
369 | color: #fff;
370 | }
371 |
372 | .buttons button#email_ver_but_resend,
373 | .buttons button#email_ver_but_verify {
374 | margin-top: 5px;
375 | }
376 |
377 | .buttons button#email_ver_but_send,
378 | .buttons button#email_ver_but_verify {
379 | background-color: #337ab7;
380 | border-color: #2e6da4;
381 | color: #fff;
382 | }
383 |
384 | .buttons button:hover#email_ver_but_send,
385 | .buttons button:hover#email_ver_but_verify {
386 | background-color: #286090;
387 | background-image: none;
388 | border-color: #204d74;
389 | color: #fff;
390 | }
391 |
392 | .buttons button#verifyCode,
393 | .buttons button#verifyPhone {
394 | background-color: #5cb85c;
395 | border-bottom: 5px solid #449d44;
396 | border-color: #4cae4c;
397 | color: #fff;
398 | font-size: 18px;
399 | height: 60px;
400 | margin-right: 9px;
401 | width: 32%;
402 | }
403 |
404 | @media(max-width:767px){
405 | .buttons button#verifyCode,
406 | .buttons button#verifyPhone {
407 | height: 50px;
408 | }
409 | }
410 |
411 | .create p {
412 | text-align: center;
413 | }
414 |
415 | .divider {
416 | margin: 0 auto;
417 | position: relative;
418 | text-align: center;
419 | text-shadow: 0 1px 0 #fff;
420 | }
421 |
422 | .divider h2 {
423 | color: #ccc;
424 | line-height: 20px;
425 | margin: 20px 0;
426 | text-align: center;
427 | text-transform: lowercase;
428 | }
429 |
430 | .divider h2:after,
431 | .divider h2:before {
432 | content: "";
433 | height: 1px;
434 | position: absolute;
435 | top: 10px;
436 | width: 40%;
437 | }
438 |
439 | .divider h2:after {
440 | background: rgb(126,126,126);
441 | background: linear-gradient(left, rgba(126,126,126,1) 0%, rgba(255,255,255,1) 100%);
442 | background: -moz-linear-gradient(left, rgba(126,126,126,1) 0%, rgba(255,255,255,1) 100%);
443 | background: -ms-linear-gradient(left, rgba(126,126,126,1) 0%, rgba(255,255,255,1) 100%);
444 | background: -o-linear-gradient(left, rgba(126,126,126,1) 0%, rgba(255,255,255,1) 100%);
445 | background: -webkit-linear-gradient(left, rgba(126,126,126,1) 0%, rgba(255,255,255,1) 100%);
446 | right: 0;
447 | }
448 |
449 | .divider h2:before {
450 | background: rgb(126,126,126);
451 | background: linear-gradient(right, rgba(126,126,126,1) 0%, rgba(255,255,255,1) 100%);
452 | background: -moz-linear-gradient(right, rgba(126,126,126,1) 0%, rgba(255,255,255,1) 100%);
453 | background: -ms-linear-gradient(right, rgba(126,126,126,1) 0%, rgba(255,255,255,1) 100%);
454 | background: -o-linear-gradient(right, rgba(126,126,126,1) 0%, rgba(255,255,255,1) 100%);
455 | background: -webkit-linear-gradient(right, rgba(126,126,126,1) 0%, rgba(255,255,255,1) 100%);
456 | left: 0;
457 | }
458 |
459 | .entry .buttons button {
460 | background-color: #337ab7;
461 | border-bottom: 5px solid #204d74;
462 | border-radius: 2px;
463 | font-size: 18px;
464 | line-height: 1.3333333;
465 | padding: 10px 16px;
466 | color: #fff;
467 | height: 60px;
468 | width: 100%;
469 | }
470 |
471 | .entry .buttons button:hover {
472 | background-color: #286090;
473 | border-color: #204d74;
474 | color: #fff;
475 | }
476 |
477 | .error.itemLevel p {
478 | color: #a94442;
479 | }
480 |
481 | .error.itemLevel p:before {
482 | content: "\e101";
483 | display: inline-block;
484 | font-family: 'Glyphicons Halflings';
485 | font-style: normal;
486 | font-weight: 400;
487 | line-height: 1;
488 | margin-right: 3px;
489 | position: relative;
490 | top: 1px;
491 | -webkit-font-smoothing: antialiased;
492 | }
493 |
494 | .highlightError {
495 | border-color: #a94442 !important;
496 | box-shadow: inset 0 1px 1px rgba(0,0,0,.075) !important;
497 | outline: 0;
498 | -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075) !important;
499 | }
500 |
501 | .image-center {
502 | display: block;
503 | margin-left: auto;
504 | margin-right: auto;
505 | text-align: center;
506 | vertical-align: middle;
507 | }
508 |
509 | .intro {
510 | display: none;
511 | }
512 |
513 | .localAccount .divider {
514 | display: none;
515 | }
516 |
517 | .login-logo {
518 | height: 140px;
519 | margin-top: -90px;
520 | }
521 |
522 | .options {
523 | display: block;
524 | margin-left: auto;
525 | margin-right: auto;
526 | padding-bottom: 10px;
527 | text-align: center;
528 | vertical-align: middle;
529 | }
530 |
531 | .panel-body{
532 | padding: 15px 30px !important;
533 | }
534 |
535 | .panel-default {
536 | border: 1px solid #ccc !important;
537 | }
538 |
539 | .phonefactor_container,
540 | .self_asserted_container,
541 | .unified_container {
542 | padding-top: 80px;
543 | }
544 |
545 | .phoneNumber .type {
546 | display: inline-block;
547 | font-weight: bold;
548 | margin-bottom: 5px;
549 | max-width: 100%;
550 | }
551 |
552 | .phoneNumbers {
553 | margin-top: 20px;
554 | }
555 |
556 | .social {
557 | margin-top: 30px;
558 | }
559 |
560 | .verify {
561 | margin-top: 5px;
562 | padding-top: 0 !important;
563 | }
564 |
565 | .working {
566 | bottom: 0;
567 | display: none;
568 | height: 2em;
569 | left: 0;
570 | margin: auto;
571 | overflow: show;
572 | position: fixed;
573 | right: 0;
574 | top: 0;
575 | width: 2em;
576 | z-index: 999;
577 | }
578 |
579 | .working:before {
580 | background-color: rgba(0,0,0,0.3);
581 | content: '';
582 | display: block;
583 | height: 100%;
584 | left: 0;
585 | position: fixed;
586 | top: 0;
587 | width: 100%;
588 | }
589 |
590 | .working:not(:required) {
591 | background-color: transparent;
592 | border: 0;
593 | color: transparent;
594 | font: 0/0 a;
595 | text-shadow: none;
596 | }
597 |
598 | .working:not(:required):after {
599 | animation: spinner 1500ms infinite linear;
600 | border-radius: 0.5em;
601 | box-shadow: rgba(0, 0, 0, 0.75) 1.5em 0 0 0, rgba(0, 0, 0, 0.75) 1.1em 1.1em 0 0, rgba(0, 0, 0, 0.75) 0 1.5em 0 0, rgba(0, 0, 0, 0.75) -1.1em 1.1em 0 0, rgba(0, 0, 0, 0.75) -1.5em 0 0 0, rgba(0, 0, 0, 0.75) -1.1em -1.1em 0 0, rgba(0, 0, 0, 0.75) 0 -1.5em 0 0, rgba(0, 0, 0, 0.75) 1.1em -1.1em 0 0;
602 | content: '';
603 | display: block;
604 | height: 1em;
605 | font-size: 10px;
606 | margin-top: -0.5em;
607 | width: 1em;
608 | -moz-animation: spinner 1500ms infinite linear;
609 | -ms-animation: spinner 1500ms infinite linear;
610 | -o-animation: spinner 1500ms infinite linear;
611 | -webkit-animation: spinner 1500ms infinite linear;
612 | -webkit-box-shadow: rgba(0, 0, 0, 0.75) 1.5em 0 0 0, rgba(0, 0, 0, 0.75) 1.1em 1.1em 0 0, rgba(0, 0, 0, 0.75) 0 1.5em 0 0, rgba(0, 0, 0, 0.75) -1.1em 1.1em 0 0, rgba(0, 0, 0, 0.5) -1.5em 0 0 0, rgba(0, 0, 0, 0.5) -1.1em -1.1em 0 0, rgba(0, 0, 0, 0.75) 0 -1.5em 0 0, rgba(0, 0, 0, 0.75) 1.1em -1.1em 0 0;
613 | }
614 |
615 | @keyframes spinner {
616 | 0% {
617 | transform: rotate(0deg);
618 | -moz-transform: rotate(0deg);
619 | -ms-transform: rotate(0deg);
620 | -o-transform: rotate(0deg);
621 | -webkit-transform: rotate(0deg);
622 | }
623 |
624 | 100% {
625 | transform: rotate(360deg);
626 | -moz-transform: rotate(360deg);
627 | -ms-transform: rotate(360deg);
628 | -o-transform: rotate(360deg);
629 | -webkit-transform: rotate(360deg);
630 | }
631 | }
632 |
633 | @-moz-keyframes spinner {
634 | 0% {
635 | transform: rotate(0deg);
636 | -moz-transform: rotate(0deg);
637 | -ms-transform: rotate(0deg);
638 | -o-transform: rotate(0deg);
639 | -webkit-transform: rotate(0deg);
640 | }
641 |
642 | 100% {
643 | transform: rotate(360deg);
644 | -moz-transform: rotate(360deg);
645 | -ms-transform: rotate(360deg);
646 | -o-transform: rotate(360deg);
647 | -webkit-transform: rotate(360deg);
648 | }
649 | }
650 |
651 | @-o-keyframes spinner {
652 | 0% {
653 | transform: rotate(0deg);
654 | -moz-transform: rotate(0deg);
655 | -ms-transform: rotate(0deg);
656 | -o-transform: rotate(0deg);
657 | -webkit-transform: rotate(0deg);
658 | }
659 |
660 | 100% {
661 | transform: rotate(360deg);
662 | -moz-transform: rotate(360deg);
663 | -ms-transform: rotate(360deg);
664 | -o-transform: rotate(360deg);
665 | -webkit-transform: rotate(360deg);
666 | }
667 | }
668 |
669 | @-webkit-keyframes spinner {
670 | 0% {
671 | transform: rotate(0deg);
672 | -moz-transform: rotate(0deg);
673 | -ms-transform: rotate(0deg);
674 | -o-transform: rotate(0deg);
675 | -webkit-transform: rotate(0deg);
676 | }
677 |
678 | 100% {
679 | transform: rotate(360deg);
680 | -moz-transform: rotate(360deg);
681 | -ms-transform: rotate(360deg);
682 | -o-transform: rotate(360deg);
683 | -webkit-transform: rotate(360deg);
684 | }
685 | }
686 |
--------------------------------------------------------------------------------