From 2769b1fe7a17331bca06af12b04885a56dbce15d Mon Sep 17 00:00:00 2001 From: Vartan Simonian Date: Mon, 29 Jun 2015 17:29:29 -0700 Subject: [PATCH] feat(fields): Generate sign in form for providers with defined fields --- boot/passport.js | 11 +- models/User.js | 4 +- models/UserApplications.js | 2 +- providers/password.js | 6 +- public/stylesheets/app.css | 277 +++++++++++++++++++++---------- public/stylesheets/providers.css | 139 ++++++++++++++++ routes/connect.js | 1 + routes/signin.js | 72 +++++--- views/signin.jade | 130 ++++++++++++--- views/signup.jade | 16 +- 10 files changed, 499 insertions(+), 159 deletions(-) create mode 100644 public/stylesheets/providers.css diff --git a/boot/passport.js b/boot/passport.js index a0f73f9e..29099ba6 100644 --- a/boot/passport.js +++ b/boot/passport.js @@ -32,12 +32,13 @@ module.exports = function (passport) { */ if (settings.providers) { - Object.keys(settings.providers).forEach(function (name) { - var providerConf = settings.providers[name] - , provider = ( providers[name] ? providers[name] : providerConf ) + Object.keys(settings.providers).forEach(function (id) { + var providerConf = settings.providers[id] + , provider = ( providers[id] ? providers[id] : providerConf ) ; - - passport.use(protocols.initialize(name, provider, providerConf)); + var strategy = protocols.initialize(id, provider, providerConf); + strategy.name = id; + passport.use(strategy); }); } diff --git a/models/User.js b/models/User.js index e684d4e0..061e2046 100644 --- a/models/User.js +++ b/models/User.js @@ -290,7 +290,7 @@ User.authenticate = function (email, password, callback) { User.lookup = function (req, info, callback) { if (req.user) { return callback(null, req.user); } - var provider = req.params.provider + var provider = req.params.provider || req.body.provider , index = User.collection + ':' + provider ; @@ -336,7 +336,7 @@ User.defineIndex({ */ User.connect = function (req, auth, info, callback) { - var provider = providers[req.params.provider]; + var provider = providers[req.params.provider || req.body.provider]; // what if there's no provider param? // Try to find an existing user. diff --git a/models/UserApplications.js b/models/UserApplications.js index 51a2cd8c..3cf95462 100644 --- a/models/UserApplications.js +++ b/models/UserApplications.js @@ -31,7 +31,7 @@ function userApplications (user, callback) { }, function (err, clients) { if (err) { return done(err); } done(null, clients); - }) + }); }, // Get the authorized scope for the user diff --git a/providers/password.js b/providers/password.js index 4f9553bc..022da9a3 100644 --- a/providers/password.js +++ b/providers/password.js @@ -5,8 +5,12 @@ module.exports = function (config) { return { id: 'password', - name: 'local', + name: 'Email and Password', protocol: 'Password', + fields: [ + { name: 'email', type: 'email' }, + { name: 'password', type: 'password' } + ], usernameField: 'email', passReqToCallback: 'true' }; diff --git a/public/stylesheets/app.css b/public/stylesheets/app.css index f83357a0..af23d4d1 100644 --- a/public/stylesheets/app.css +++ b/public/stylesheets/app.css @@ -7,6 +7,10 @@ body { box-sizing: border-box; } +[hidden] { + display: none !important; +} + :hover { transition: .3s background; } @@ -48,6 +52,127 @@ a { line-height: 0em; } +.blur-transition { + transition: .3s filter linear; + -webkit-transition: .3s -webkit-filter linear; + -moz-transition: .3s -moz-filter linear; + -moz-transition: .3s filter linear; + -ms-transition: .3s -ms-filter linear; + -o-transition: .3s -o-filter linear; + -webkit-filter: blur(0); + -moz-filter: blur(0); + -o-filter: blur(0); + -ms-filter: blur(0); + filter: blur(0); +} + +.blur { + -webkit-filter: blur(3px); + -moz-filter: blur(3px); + -o-filter: blur(3px); + -ms-filter: blur(3px); + filter: blur(3px); +} + +.screen, .screen::before { + position: fixed; + top: 0; + left: 0; + bottom: 0; + right: 0; +} + +.screen { + padding: 16px; + display: -webkit-flex; + display: flex; + -webkit-align-items: center; + align-items: center; + -webkit-justify-content: center; + justify-content: center; + -webkit-transition: .5s opacity linear; + transition: .5s opacity linear; + opacity: 1; +} + +.screen[hidden] { + opacity: 0; +} + +.screen::before { + content: ''; + background-color: rgba(255, 255, 255, 0.5); +} + +.loader { + position: relative; + margin: 0px auto; + width: 56px; + height: 56px; +} + +.circular { + -webkit-animation: rotate 2s linear infinite; + animation: rotate 2s linear infinite; + height: 56px; + position: relative; + width: 56px; +} + +.path { + stroke-dasharray: 1,200; + stroke-dashoffset: 0; + stroke-linecap: square; + stroke: #1976d2; + -webkit-animation: dash 1.5s ease-in-out infinite; + animation: dash 1.5s ease-in-out infinite; +} + +@-webkit-keyframes rotate { + 100% { + -webkit-transform: rotate(360deg); + transform: rotate(360deg); + } +} + +@keyframes rotate { + 100% { + -webkit-transform: rotate(360deg); + transform: rotate(360deg); + } +} + +@-webkit-keyframes dash { + 0% { + stroke-dasharray: 1,200; + stroke-dashoffset: 0; + } + 50% { + stroke-dasharray: 89,200; + stroke-dashoffset: -35; + } + 100% { + stroke-dasharray: 89,200; + stroke-dashoffset: -124; + } +} + +@keyframes dash { + 0% { + stroke-dasharray: 1,200; + stroke-dashoffset: 0; + } + 50% { + stroke-dasharray: 89,200; + stroke-dashoffset: -35; + } + 100% { + stroke-dasharray: 89,200; + stroke-dashoffset: -124; + } +} + + .anvilform { width: 500px; margin: 0 auto; @@ -85,8 +210,8 @@ a { content: 'sign in with'; } -.anvilform .textsep.signupwith:before { - content: 'sign up with'; +.anvilform .textsep.signup:before { + content: 'sign up'; } .anvilform .textsep.or:before { @@ -97,7 +222,7 @@ a { background: #ffffff; border: 1px solid #ccc; padding: 30px; - width: 400px; + width: 420px; margin: 0 auto; position: relative; } @@ -107,7 +232,6 @@ a { color: #fff; padding: 10px; position: relative; - top: -16px; } .anvilform .panel .providers { @@ -115,7 +239,9 @@ a { list-style-type: none; } -.anvilform form input[type="text"], .anvilform form input[type="password"] { +.anvilform form input[type="text"], .anvilform form input[type="password"], +.anvilform form input[type="email"], .anvilform form input[type="tel"], +.anvilform form input:not([type]) { background: #fafafa; border: 1px solid #ccc; height: 65px; @@ -135,7 +261,8 @@ a { margin-right: 0; } -.anvilform button { +.anvilform button, .anvilform input[type="button"], +.anvilform input[type="submit"] { height: 65px; margin: 10px 2px 0 0; border: 0px solid #ccc; @@ -159,31 +286,37 @@ a { user-select: none; } -.anvilform button:hover { +.anvilform button:hover, .anvilform input[type="button"]:hover, +.anvilform input[type="submit"]:hover { background: #aaa; } -.anvilform button.callout { +.anvilform button.callout, .anvilform input[type="button"].callout, +.anvilform input[type="submit"].callout { background: #21d782; font-weight: 400; color: #fff; } -.anvilform button.full { +.anvilform button.full, .anvilform input[type="button"].full, +.anvilform input[type="submit"].full { width: 100%; } -.anvilform button.smalltext { +.anvilform button.smalltext, .anvilform input[type="button"].smalltext, +.anvilform input[type="submit"].smalltext { font-size: 12px; position: relative; top: -7px; } -.anvilform button.callout:hover { +.anvilform button.callout:hover, .anvilform input[type="button"].callout:hover, +.anvilform input[type="submit"].callout:hover { background: #4ddf9b; } -.anvilform button:last-child { +.anvilform button:last-child, .anvilform input[type="button"]:last-child, +.anvilform input[type="submit"]:last-child { margin-right: 0px; } @@ -193,9 +326,19 @@ a { font-weight: 100; } +.form-provider-name { + color: #424242; + text-align: center; + padding: 4px; +} + +.form-providers { + margin-top: 2px; +} + .providers li { display: inline-block; - margin: 0 2px 4px; + margin: 4px; height: 63px; width: 63px; } @@ -208,93 +351,47 @@ a { height: 100%; width: 100%; text-align: center; - transition: .3s background; -} - -.provider.angellist { - background: #e9eaec; -} - -.provider.buffer { - background: #f4faff; -} - -.provider.dropbox { - background: #2895f1; -} - -.provider.google { - background: #dd4b39; -} - -.provider.google:hover { - background: #a6382b; -} - -.provider.facebook { - background: #3b5998; -} - -.provider.facebook:hover { - background: #2c4372; -} - -.provider.foursquare { - background: #0732a2; -} - -.provider.mailchimp { - background: #2c9ab7; -} - -.provider.reddit { - background: #cee3f8; -} - -.provider.twitch { - background: #6441a5; -} - -.provider.twitter { - background: #55acee; -} - -.provider.twitter:hover { - background: #4993CC; -} - -.provider.github, .provider.codepen { - background: #000; -} - -.provider.github:hover, .provider.codepen:hover { - background: #333; -} - -.provider.dribbble { - background: #ea4c89; + -webkit-transition: .3s -webkit-box-shadow; + -moz-transition: .3s -moz-box-shadow; + transition: .3s box-shadow; + position: relative; + outline: none; } -.provider.dribbble:hover { - background: #c06185; +.provider.selected-provider { + -webkit-box-shadow: 0px 0px 6px 1px #ff0; + -moz-box-shadow: 0px 0px 6px 1px #ff0; + box-shadow: 0px 0px 6px 1px #ff0; } -.provider.instagram { - background: #517fa4; +.provider:focus { + -webkit-box-shadow: 0px 0px 6px 1px #3680e0; + -moz-box-shadow: 0px 0px 6px 1px #3680e0; + box-shadow: 0px 0px 6px 1px #3680e0; } -.provider.instagram:hover { - background: #24557b; +.provider::after, .provider::before { + display: block; + position: absolute; + top: 0; + left: 0; + bottom: 0; + right: 0; + content: ''; } -.provider.soundcloud { - background: #f50; +.provider::before { + transition: .3s opacity; + background-color: #000; + opacity: 0; } -.provider.wordpress { - background: #21759b; +.provider:hover::before, .provider:focus::before { + opacity: 0.2; } -.provider.wordpress:hover { - background: #195874; +.provider::after { + background-image: url('data:image/svg+xml,%3Csvg%20fill%3D%22%23fff%22%20width%3D%2248%22%20height%3D%2248%22%20viewBox%3D%220%200%2024%2024%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d%3D%22M0%200h24v24H0z%22%20fill%3D%22none%22%2F%3E%3Cpath%20d%3D%22M18%208h-1V6c0-2.76-2.24-5-5-5S7%203.24%207%206v2H6c-1.1%200-2%20.9-2%202v10c0%201.1.9%202%202%202h12c1.1%200%202-.9%202-2V10c0-1.1-.9-2-2-2zm-6-5.1c1.71%200%203.1%201.39%203.1%203.1v2H9V6h-.1c0-1.71%201.39-3.1%203.1-3.1zM18%2020H6V10h12v10zm-6-3c1.1%200%202-.9%202-2s-.9-2-2-2-2%20.9-2%202%20.9%202%202%202z%22%2F%3E%3C%2Fsvg%3E'); + background-repeat: no-repeat; + background-position: center; } diff --git a/public/stylesheets/providers.css b/public/stylesheets/providers.css new file mode 100644 index 00000000..cfa750c2 --- /dev/null +++ b/public/stylesheets/providers.css @@ -0,0 +1,139 @@ +.provider.password::after { + background-image: url('data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2248%22%20height%3D%2248%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22%23fff%22%3E%3Cpath%20d%3D%22M15.932%2014.145q-.11.33-.192.66-.055.33-.055.605%200%20.275.11.468.137.164.412.164.633%200%201.265-.44.66-.467%201.155-1.264.523-.798.826-1.87.33-1.1.33-2.365%200-1.54-.578-2.75-.578-1.238-1.595-2.09-.99-.853-2.337-1.293-1.32-.467-2.833-.467-1.76%200-3.217.66-1.43.632-2.476%201.76-1.044%201.1-1.622%202.612-.577%201.485-.577%203.217%200%202.008.605%203.63.632%201.623%201.705%202.75%201.1%201.128%202.585%201.76%201.512.606%203.244.606%201.816%200%203.438-.715%201.623-.715%202.75-2.008H20.8q-1.32%202.145-3.493%203.328-2.172%201.182-4.73%201.182-2.117%200-3.987-.77-1.87-.797-3.272-2.2-1.375-1.402-2.173-3.355-.797-1.953-.797-4.318%200-2.09.797-3.877.825-1.815%202.2-3.162%201.375-1.348%203.19-2.118%201.843-.77%203.905-.77%201.843%200%203.493.605%201.677.578%202.942%201.65%201.265%201.073%202.007%202.558.77%201.485.77%203.245%200%202.062-.715%203.602-.687%201.54-1.705%202.558-.99%201.017-2.117%201.54-1.127.495-1.952.495-.633%200-1.1-.385-.468-.413-.523-1.21h-.055q-.55.632-1.375%201.1-.825.467-1.705.467-.852%200-1.595-.33-.742-.357-1.293-.935-.522-.605-.825-1.375-.302-.798-.302-1.678%200-1.32.44-2.584.44-1.265%201.21-2.283.797-1.017%201.897-1.622%201.128-.633%202.476-.633.962%200%201.76.468.797.467%201.32%201.512l.494-1.567h1.925l-1.98%207.232zm-5.005%201.732q.825%200%201.513-.467.687-.495%201.182-1.21.495-.743.77-1.595.303-.88.303-1.623%200-.495-.193-.935-.165-.44-.467-.77-.275-.33-.688-.522-.385-.192-.825-.192-.852%200-1.567.467-.688.468-1.183%201.182-.495.688-.77%201.54-.275.853-.275%201.623%200%201.1.578%201.815.605.687%201.622.687z%22%2F%3E%3C%2Fsvg%3E'); +} + +.provider.angellist { + background: #e9eaec; +} + +.provider.angellist::after { + background-image: url('data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20height%3D%2248%22%20width%3D%2248%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22%23424242%22%3E%3Cpath%20d%3D%22M9.195%201.3c-.537%200-.914.163-1.218.503-.287.323-.446.778-.446%201.418%200%20.418.167%201.24.51%202.395a.3.3%200%200%201%200%20.002c.298%201.012.792%202.503%201.48%204.47a.3.3%200%200%201-.39.38c-.308-.12-.56-.174-.753-.174-.35%200-.778.198-1.275.666-.47.496-.67.933-.67%201.3%200%20.19.1.6.324%201.16a.3.3%200%200%201-.197.4c-.69.19-1.156.48-1.437.86a.3.3%200%200%201-.006.01c-.348.434-.54%201.064-.54%201.925%200%20.713.175%201.438.532%202.18.34.694.86%201.375%201.574%202.045.662.62%201.394%201.073%202.203%201.36a.3.3%200%200%201%20.01.003c.842.332%201.716.498%202.628.498%202.318%200%204.192-.774%205.692-2.342%201.47-1.568%202.205-3.57%202.205-6.07%200-1.265-.2-2.173-.537-2.712-.314-.502-.82-.843-1.586-1.03a.3.3%200%200%201-.21-.392c.646-1.823%201.12-3.257%201.418-4.296.343-1.214.506-2.04.506-2.356%200-.563-.15-.98-.444-1.31a.3.3%200%200%201-.004-.006c-.275-.323-.623-.477-1.13-.477-.197%200-.425.09-.696.32-.27.233-.57.603-.883%201.11-.626%201.016-1.306%202.577-2.02%204.665V7.8c-.09.263-.175.508-.265.73a.3.3%200%200%201-.56-.012l-.367-1.057c-.723-2.11-1.395-3.688-2-4.718-.3-.514-.586-.89-.838-1.125-.252-.234-.455-.318-.612-.318zm-.13.804c.35%200%20.604.253.814.59v.003c.27.407.575%201.147.98%202.3a.3.3%200%200%201%200%20.003l1.488%204.314a.3.3%200%200%201-.31.397c-.316-.028-.526-.042-.534-.042-.085%200-.17-.004-.254-.012-.075-.007-.135-.01-.113-.01-.226%200-.374.04-.45.086a.3.3%200%200%201-.435-.152c-.634-1.714-1.11-3.083-1.428-4.113v.006c-.35-1.063-.53-1.843-.53-2.403%200-.25.065-.488.212-.677.13-.172.345-.29.56-.29zm8.37.474c.214%200%20.446.095.567.285.145.195.207.438.207.703%200%20.19-.054.402-.146.754-.09.353-.227.82-.408%201.405a.3.3%200%200%201%200%20.004c-.347%201.083-.844%202.516-1.492%204.3a.3.3%200%200%201-.334.195l-1.445-.26a.3.3%200%200%201-.23-.393l1.402-4.033v.003c.348-1.046.657-1.785.96-2.248.13-.207.26-.373.405-.498.145-.126.317-.22.514-.22zm-5.628%207.85c1.3%200%202.53.132%203.69.396%201.153.25%201.942.526%202.41.926a.3.3%200%200%201%20.003.002c.256.22.423.53.506.893.095.398.14.924.14%201.597%200%202.173-.652%204.007-1.954%205.444a.3.3%200%200%201-.002%200c-1.315%201.448-2.973%202.19-4.903%202.19-1.074%200-2.05-.21-2.914-.634a.3.3%200%200%201-.002-.002c-.846-.423-1.578-1.038-2.192-1.832a.3.3%200%200%201-.004-.006c-.385-.518-.68-1.018-.88-1.507-.21-.513-.323-.97-.323-1.387%200-.53.15-1.002.456-1.375.292-.357.642-.584%201.03-.584.15%200%20.25.054.355.12.1.062.2.146.31.248.28.23.61.6%201.02%201.127a.3.3%200%200%201%200%20.002c.42.55.687.93.823%201.184.177.31.295.503.295.766%200%20.212-.11.318-.22.428a.3.3%200%200%201-.03.03c-.158.117-.35.187-.545.187-.66%200-1.256-.516-1.88-1.392l-.005-.006c-.086-.115-.3-.383-.498-.63-.002%200-.04.058-.04.046a.3.3%200%200%201-.016.063c-.032.084-.045.163-.045.24%200%20.525.286%201.097.942%201.726.654.612%201.262.888%201.842.888.378%200%20.684-.118.965-.375.277-.255.397-.525.397-.88%200-.086-.007-.156-.016-.2a.3.3%200%200%201%20.294-.358h.237c.12%200%20.21-.022.165-.006a.3.3%200%200%201%20.394.27c.027.69.156%201.235.372%201.642a.3.3%200%200%201%20.002.002c.205.39.503.57.903.654v-.086c0-.035-.066-.366-.22-.832-.15-.45-.233-.845-.233-1.195%200-.568.186-1.153.537-1.747a.3.3%200%200%201%20.003-.002c.365-.608.852-1.134%201.455-1.57l.004-.003s0-.002.002-.002c.287-.22.687-.41%201.217-.6.457-.168.733-.35.94-.52-.04-.137-.082-.308-.1-.326a.3.3%200%200%201-.04-.05c-.046-.072-.083-.1-.245-.1h-.603c-1.67%200-2.874-.14-3.668-.474-.73-.272-1.182-.828-1.182-1.527%200-.144.01-.268.053-.39.04-.11.132-.223.256-.29.203-.16.436-.182.747-.182zm-3.323.756c.31%200%20.6.154.86.394.29.24.58.608.904%201.125a.3.3%200%200%201%20.002.004c.37.605.707%201.28%201.014%202.027a.3.3%200%200%201%20.002.004c.13.335.23.61.3.828.067.22.108.37.108.532%200%20.204-.094.39-.227.543a.3.3%200%200%201-.013.014c-.136.136-.313.24-.512.24-.34%200-.605-.198-.97-.5a.3.3%200%200%201-.005-.007c-.38-.335-.792-.792-1.246-1.377a.3.3%200%200%201%200-.004c-.453-.598-.784-1.094-1-1.508a.3.3%200%200%201%200-.004c-.238-.474-.378-.795-.378-1.105%200-.3.17-.55.412-.794.226-.24.458-.412.75-.412zm3.674%202.587a.3.3%200%200%201%20.11.017c.102.034.307.084.59.14.065.014.166.03.306.052.147.02.26.04.342.057a.3.3%200%200%201%20.12.533c-.27.2-.54.458-.817.776a.3.3%200%200%201-.51-.094c-.142-.387-.28-.743-.407-1.072a.3.3%200%200%201%20.265-.41z%22%2F%3E%3Cpath%20d%3D%22M0%200h24v24H0z%22%20fill%3D%22none%22%2F%3E%3C%2Fsvg%3E'); +} + +.provider.buffer { + background: #f4faff; +} + +.provider.buffer::after { + background-image: url('data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2248%22%20height%3D%2248%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22%23424242%22%3E%3Cpath%20d%3D%22M1.39%206.758l9.693%204.68c.252.12.59.184.923.184.332%200%20.67-.063.922-.183l9.694-4.683c.51-.247.51-.648%200-.894l-9.694-4.68c-.252-.12-.59-.184-.922-.184-.333%200-.67.062-.923.182L1.39%205.862c-.51.248-.51.65%200%20.896z%22%2F%3E%3Cpath%20d%3D%22M22.622%2011.553l-2.005-.968c-.235-.115-.298-.11-.545.006-.246.116-7.15%203.45-7.15%203.45-.257.12-.59.184-.922.184-.332%200-.67-.063-.922-.183%200%200-6.72-3.242-7.036-3.397-.344-.166-.44-.166-.75-.017l-1.914.922c-.51.247-.51.648%200%20.894l9.694%204.682c.252.12.59.183.922.183.333%200%20.67-.063.923-.183l9.693-4.68c.522-.24.522-.643.012-.89z%22%2F%3E%3Cpath%20d%3D%22M22.622%2017.242s-1.77-.853-2.005-.968c-.235-.115-.298-.11-.545.006-.246.114-7.144%203.454-7.144%203.454-.258.12-.59.184-.922.184-.333%200-.67-.063-.923-.184%200%200-6.72-3.242-7.035-3.397-.344-.166-.44-.166-.75-.017l-1.914.922c-.51.247-.51.648%200%20.894l9.694%204.68c.252.127.59.184.922.184.332%200%20.67-.063.922-.183l9.694-4.68c.516-.247.516-.648.006-.895z%22%2F%3E%3C%2Fsvg%3E'); +} + +.provider.dropbox { + background: #2895f1; +} + +.provider.dropbox::after { + background-image: url('data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2248%22%20height%3D%2248%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22%23fff%22%3E%3Cpath%20d%3D%22M7.68%202.5L12%206.09l4.32-3.59%206.18%204-4.27%203.44%204.27%203.42-6.18%204.03L12%2013.78l-4.32%203.61-6.18-4.03%204.27-3.42L1.5%206.5l6.18-4M12%206.09L5.77%209.94%2012%2013.78l6.23-3.84L12%206.09m0%208.47l4.35%203.6%201.85-1.21v1.35L12%2022l-6.18-3.7v-1.35l1.86%201.21%204.32-3.6z%22%2F%3E%3Cpath%20d%3D%22M0%200h24v24H0z%22%20fill%3D%22none%22%2F%3E%3C%2Fsvg%3E'); +} + +.provider.google { + background: #dd4b39; +} + +.provider.google::after { + background-image: url('data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2248%22%20height%3D%2248%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22%23fff%22%3E%3Cpath%20d%3D%22M12.3%2022.3c-3.33%200-5.45-1.49-5.45-3.7%200-2.2%201.96-2.91%202.65-3.16%201.3-.44%203-.49%203.27-.49.3%200%20.46%200%20.73.02%202.34%201.69%203.35%202.44%203.35%204.03%200%201.77-1.82%203.3-4.55%203.3M8.65%204.31c0-2.21%201.31-3.21%202.69-3.21%202.66%200%204.01%203.45%204.01%205.53%200%202.57-2.07%203.07-2.89%203.07-2.46%200-3.81-3.06-3.81-5.39m7.65%209.14l-1.08-.85c-.36-.3-.82-.69-.82-1.42s.55-1.29.97-1.62c1.31-1.02%202.57-2.1%202.57-4.34%200-2.07-1.27-3.26-2.04-3.92h1.75L18.9.05h-6.23c-4.36%200-6.6%202.71-6.6%205.72%200%202.33%201.79%204.83%204.98%204.83h.8c-.13.35-.35.84-.35%201.3%200%201.01.42%201.43.92%202-1.42.1-4.01.43-5.92%201.6-1.86%201.1-2.3%202.63-2.3%203.75%200%202.3%202.06%204.5%206.57%204.5%205.35%200%208.03-2.96%208.03-5.88%200-2.16-1.13-3.27-2.5-4.42z%22%2F%3E%3Cpath%20d%3D%22M0%200h24v24H0z%22%20fill%3D%22none%22%2F%3E%3C%2Fsvg%3E'); +} + +.provider.facebook { + background: #3b5998; +} + +.provider.facebook::after { + background-image: url('data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2248%22%20height%3D%2248%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22%23fff%22%3E%3Cpath%20d%3D%22M17%202v4h-2c-.69%200-1%20.81-1%201.5V10h3v4h-3v8h-4v-8H7v-4h3V6a4%204%200%200%201%204-4h3z%22%2F%3E%3Cpath%20d%3D%22M0%200h24v24H0z%22%20fill%3D%22none%22%2F%3E%3C%2Fsvg%3E'); +} + +.provider.foursquare { + background: #0732a2; +} + +.provider.foursquare::after { + background-image: url('data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2248%22%20height%3D%2248%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22%23fff%22%3E%3Cpath%20d%3D%22M17%205l-.43%202.5c-.07.23-.37.5-.66.5H12c-.47%200-1.05.32-1.05.79v.41c0%20.47.58.8%201.05.8h3.28c.33%200%20.65.36.58.71-.07.36-.92%202.57-.96%202.79-.04.17-.26.5-.65.5h-2.88c-.52%200-.68.07-1.03.5-.34.44-3.07%203.6-3.07%203.6-.03.03-.27-.06-.27-.1V5c0-.3.61-1%201-1h8.5c.32%200%20.58.61.5%201m0%209.45c.11-.48%201.78-7.73%202.22-9.9M17.58%202H6.91C5.43%202%205%203.11%205%203.8v16.96c0%20.78.42%201.08.66%201.17.24.1.89.18%201.28-.27%200%200%204.71-5.44%204.8-5.53.13-.13.13-.13.26-.13h3.26c1.37%200%201.59-1%201.74-1.55.11-.48%201.78-7.73%202.22-9.9.34-1.66-.08-2.55-1.64-2.55z%22%2F%3E%3Cpath%20d%3D%22M0%200h24v24H0z%22%20fill%3D%22none%22%2F%3E%3C%2Fsvg%3E'); +} + +.provider.mailchimp { + background: #2c9ab7; +} + +.provider.mailchimp::after { + background-image: url('data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2248%22%20height%3D%2248%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22%23fff%22%3E%3Cpath%20d%3D%22M12.37%201c-.33%200-.68.05-1.06.15-.75.2-1.6.57-2.48%201.1-.88.53-1.8%201.22-2.7%202.03-1.1%201-2%202.06-2.65%203.06-.33.5-.6.98-.8%201.45-.2.45-.33.9-.38%201.3-.05.3-.04.56.02.82.02.1.06.22.1.32.07.13.15.25.25.36l.86.9c-.62%201.1-.37%202.28.02%203.06.5.8%201.26%201.25%202.17%201.43v.02l.2.48c1.03%202.85%203.87%205.05%207.1%205.45%203.25.4%206.7-1.75%208.12-4.66l.16-.34c.14-.14.25-.3.32-.45.1-.17.12-.37.1-.53.02-.23-.1-.4-.2-.58-.15-.2-.33-.24-.54-.28.1-.42-.03-.74-.1-.9.12-.1.2-.2.28-.3l.02-.02c.06-.08.1-.17.12-.25.08-.3.03-.56-.07-.84-.04-.08-.1-.18-.18-.26-.08-.1-.17-.17-.3-.23-.24-.34-.5-.6-.74-.8-.33-.28-.67-.52-1-.73v-.15c0-.53.05-1.1.08-1.56V9.6c0-.56-.15-.97-.44-1.37-.07-.4-.26-.73-.43-1.05l-.14-.2c.98-1.15%202.45-3.07%201.3-4.55-.45-.5-1.05-.72-1.58-.8-1.37-.16-2.9.5-3.32.7l-.73-.8V1.5c-.14-.15-.3-.23-.47-.32-.18-.08-.37-.13-.57-.16L12.36%201zm.66.64l.4-.1%201.1%201.28c.86-.4%201.93-.72%202.77-.73.37-.03.7.08%201.03.2.3.06.48.3.66.44.92%201.2-.62%203.15-1.12%203.72l-.2.2c-.23-.7-.84-1.15-1.34-1.33.1.1.1.23.14.34-.17-.13-.38-.23-.54-.32-.25-.12-.5-.2-.74-.28.14.1.16.22.25.35-.23-.1-.45-.14-.62-.2-.34-.05-.7-.07-1.03-.04-.03%200-.07%200-.1.02.12.03.24.12.34.18l.15.16.06.1-.1-.04c-.8-.16-1.66-.02-2.43.27.25-.03.48%200%20.73.02-.77.28-1.54.7-2.15%201.1-.63.45-1.23.96-1.77%201.53L8.1%209c-.6.73-1.07%201.4-1.48%202.14-.54-.13-1.12-.03-1.6.12-.46.2-.83.52-1.15.83-.3-.28-.55-.57-.82-.85-.07-.08-.13-.17-.17-.28-.03-.06-.04-.12-.05-.17-.05-.2-.05-.4-.02-.65.05-.34.16-.73.35-1.16.28-.66.72-1.38%201.27-2.1.28-.38.6-.74.92-1.1.35-.4.73-.77%201.13-1.13C7.6%203.64%208.8%202.8%209.92%202.24c1.02-.5%202.04-.9%203.1-.6zm.4-.06l-.48.13-.17.4.03.33-1-.5-.5.16v.6l.97%201.18c-.78.34-1.57.8-2.23%201.27-1.15.83-2.13%201.7-3.02%202.7-.6.66-1.12%201.34-1.52%201.98-.3.5-.56%201-.72%201.44.6-.22%201.23-.33%201.8-.24.42-.73.95-1.5%201.44-2.07l.44-.5C9%207.9%209.62%207.4%2010.26%206.95c.58-.4%201.2-.72%201.82-1-.3%200-.6.06-.92.12l.27-.14c.84-.44%201.7-.53%202.6-.42%200-.02-.03-.04-.05-.05-.2-.16-.42-.2-.67-.28.52-.12%201.08-.07%201.54-.03l.4.1c-.13-.16-.3-.25-.5-.37.44.08.87.25%201.24.4.12.05.22.12.33.18-.06-.13-.18-.23-.26-.33.76.24%201.47.75%201.7%201.38%200-.03.04-.06.08-.1.48-.56%202-2.46%201.1-3.6-.18-.23-.38-.35-.62-.43-.8-.3-1.5-.04-2.17.17-.95.3-1.83.74-2.7%201.22-.57.32-1.15.7-1.66%201.07-.62.44-1.22.92-1.8%201.42-.94.82-1.82%201.68-2.62%202.53.83-1.02%201.84-2.03%202.8-2.9.5-.45%201.02-.9%201.56-1.3.25-.18.5-.36.73-.5.12-.1.25-.2.4-.27l-1.5-1.7.48-.17%201%20.5.25.13-.1-.85.47-.1%201.07%201.24-1.07-1.26zm.2.94l.1.74.38-.2zm-1.02.3l.65.72.4-.22zm3.04%203.64v.08c-.18.03-.33.2-.45.3.4-.2.76-.23%201.22-.27l-.25.1-.24.12c.48-.06%201%20.1%201.42.2.5.4.72.95.85%201.45.24.3.32.68.4%201.02.02.2%200%20.38%200%20.57l-.08%201.17c-.04.78.02.86.5%201.15.48.32%201.06.83%201.35%201.25l.03.05c.3.15.4.4.47.74l-.02.02c-.04.04-.08.08-.13.1l.1-.12c-1.56%201.4-3.27%201.8-4.57%201.88-.7.05-1.38-.02-2.06-.1-.1%200-.2%200-.28.03-.18.06-.33.18-.44.34-.14.2-.23.45-.23.72.08%201.04%201.03%201.4%201.93%201.5.53.08%201.14.07%201.8-.02%202-.36%202.93-1%203.37-1.45.17-.18.3-.34.4-.5l-.07-.24c.4-.06.5.15.57.48%200%20.2-.1.3-.16.44-.6%201.06-1.23%201.88-2.1%202.54-.6.44-1.32.72-2%20.94-.7.2-1.43.3-2.2.3-1.27%200-2.43-.3-2.96-.6l.23.4c-.12-.02-.2-.12-.28-.17-.16-.15-.36-.4-.63-.73-.05-.07-.1-.15-.17-.22.05.18.17.43.17.43l.17.34c-.23-.24-.4-.52-.57-.76-.15-.24-.25-.45-.38-.7%200%20.2.06.36.1.48.1.17.17.33.26.5l-.5-.53c-.2-.23-.33-.48-.47-.72-.22-.45-.34-.93-.4-1.35-.02-.56.12-1.15.3-1.6.16-.4.4-.75.7-1-.15-.25-.3-.5-.42-.74l.03.67c-.3-.42-.33-1-.43-1.5-.02.1-.04.28-.04.46v.37l-.1-.37c-.08-.5-.1-.9-.1-1.37-.1.26-.1.5-.1.76l-.1.02c-.1-.9-.16-1.8.04-2.58.1-.47.38-.92.6-1.3.57-.8%201.48-1.4%202.27-1.76.75-.32%201.52-.52%202.23-.7.13-.1.27-.2.4-.27.28-.14.55-.23.82-.26h.14zm-.3.12c-.2.04-.4.12-.6.23-.17.1-.3.2-.43.3-.78.16-1.6.4-2.22.68-1%20.44-1.63.96-2.24%201.74-.28.4-.45.84-.58%201.27-.16.63-.15%201.36-.12%201.93%200%20.05%200%20.1.02.15.03-.24.13-.4.22-.6l.02.13c0%20.4.04.8.07%201.15.02-.18.07-.23.15-.3.1.45.15.9.3%201.35-.04-.22-.02-.44-.02-.67.2.4.4.76.6%201.1-.33.3-.55.7-.74%201.03-.23.5-.28%201.03-.27%201.56.04.48.2.95.37%201.32.16.3.35.56.54.8-.1-.26-.08-.48-.1-.74.14.17.22.34.32.52.1.2.22.38.33.56-.05-.12-.02-.24-.03-.35.13.12.2.25.32.4.24.3.44.53.58.67l-.26-.46c.9.7%202.3.82%203.24.83.44%200%20.88-.03%201.3-.1-1.07.1-1.95-.14-2.64-.64l.26.43c-.2-.07-.8-.83-.8-.83-.04.08.14.44.14.44-.25-.28-.46-.63-.6-.96-.05.4.18.7.18.7-.6-.63-.76-1.54-.8-1.84l-.1-.57c-.18.38-.06.77-.06.77-.13-.34-.2-.67-.23-.97-.02-.4-.03-.77%200-1.2-.15.25-.17.53-.22.76-.16-.67.2-1.52.2-1.52-.24.22-.4.5-.47.85-.05-.7.28-1.28.76-1.65.28-.23.6-.4.87-.5.1-.05.2-.1.28-.12l.22-.06c.04%200%20.08-.02.1-.03.02%200%20.03-.02.03-.02-.18-.05-.35-.12-.52-.2-.42-.2-1-.56-1.37-1.18-.28-.5-.32-.97-.3-1.5.05-.55.3-1%20.57-1.44.83-1.05%201.7-1.03%202.84-.77.45.14.9.16%201.32.16.47-.12.8-.37%201.17-.6.42-.28.78-.35%201.17-.18-.1-.56-.43-1.13-.8-1.33-.58-.16-1.08-.26-1.6-.18.04-.1.15-.15.2-.2-.3.1-.64.18-.9.25.08-.15.23-.28.35-.4zm2.32%202.68c-.05%200-.1%200-.16.02%200%200-.27.06-.43.24%200%200%20.47-.2.97.15.05.04.1.07.14.12.05.03.1.08.14.13-.06-.3-.12-.4-.3-.57-.1-.06-.2-.1-.36-.1zm-4.2.63c-.32.08-.54.25-.7.43-.12.16-.27.47-.3.66l-.04.35v.23l.18-.34c.15-.25.3-.46.47-.62.3-.32.63-.5.87-.58.27-.1.45-.1.45-.1-.07-.04-.14-.06-.2-.08l-.14-.02c-.2-.04-.4%200-.6.05zm4.04.86c-.17%200-.3.33-.26.7v.17c.27%200%20.45.1.57.2.04-.1.05-.25.04-.4%200-.16-.04-.3-.1-.43%200-.06-.1-.25-.24-.24zm-2.95.6c-.22%200-.36.45-.3.95.03.27.1.5.2.65l.6-.15c.03-.17.03-.37%200-.6-.05-.5-.28-.88-.5-.85zm-6.83.68c-.38.66-.7%201.43-.84%202.07.08-.2.23-.3.37-.46-.17.46-.23.9-.28%201.37.02-.12.14-.2.23-.24.06.5.16%201%20.25%201.4-1.88.87-3.37-.14-3.77-1.55-.14-.8-.02-1.35.33-1.98.15-.22.35-.4.52-.56.4-.3.85-.47%201.25-.56.77-.05%201.36.18%201.95.54zm-1.94-.45c-.47.08-.9.3-1.2.52-.22.16-.34.35-.5.55-.37.55-.4%201.36-.32%201.92.5%201.62%202.07%202.22%203.6%201.53-.08-.32-.12-.55-.18-.85-.53.2-.96.15-1.32-.08-.33-.2-.46-.46-.57-.75-.1-.23-.08-.45-.04-.7.1.3.24.52.4.7.02-.02.05-.02.08-.03-.03-.1-.04-.23-.05-.33l.15.32c.08.17.2.28.32.42l-.03-.16c-.03-.1-.04-.22-.05-.33.1.24.2.43.28.54l.2.23-.07-.4v-.24c.14.28.23.55.45.68v-.1c-.25-.28-.37-.63-.5-1-.05.25-.05.5-.02.73-.07-.1-.18-.27-.26-.5-.06-.16-.1-.3-.14-.47l-.03.4.02.3c-.12-.16-.2-.36-.26-.52-.06-.13-.1-.26-.13-.4l-.02.23c0%20.15%200%20.3.02.44-.15-.2-.23-.46-.3-.65.05-.38.45-.64.72-.8.43-.16.8-.1%201.15.02.14-.28.3-.55.4-.77-.54-.36-1.24-.52-1.82-.48zm11.63.28h-.17c-.54.05-.72.5-.76.63v.06l-.02.03.04.02c-.04.05-.03.1%200%20.18.04.04.08.1.13.12.1.06.2.07.26.05h.02l.05.03c.06%200%20.18.04.3.04.22%200%20.38-.07.5-.2%200-.03.02-.05.03-.07.05%200%20.1-.06.13-.15v-.03c.02-.08.02-.15%200-.2%200-.03%200-.08-.02-.1-.02-.13-.12-.4-.5-.42zm0%20.04c.35.03.44.27.47.38v.05c-.05-.03-.13.04-.16.15v.1c0%20.08%200%20.14.06.17l-.04.04c-.2.28-.6.2-.74.15h-.05c0-.02.02-.03.03-.04.03-.04%200-.1-.03-.18l-.12-.12c-.1-.07-.22-.07-.3-.02v-.06c.06-.14.23-.55.73-.6h.17zm2.9%203.3v.02c.06.14.14.37.1.67%200%20.1-.03.18-.05.26.2.18.23.4.3.6-.15.15-.33.3-.54.44-.84.58-2.08%201.07-3%201.2-.67.1-1.32.12-1.88.05-.32-.04-.6-.1-.85-.2-.15-.1-.4-.33-.44-.82.7.26%201.5.3%202.18.28.75-.04%201.47-.17%202.13-.5-.67.1-1.42.22-2.13.26-.8.03-1.44-.08-2.13-.36v-.05c.12-.3.34-.42.62-.5.83.1%201.66.1%202.44-.03%201.15-.18%202.2-.6%203.14-1.22l.1-.08z%22%2F%3E%3Cpath%20d%3D%22M0%200h24v24H0z%22%20fill%3D%22none%22%2F%3E%3C%2Fsvg%3E') +} + +.provider.reddit { + background: #cee3f8; +} + +.provider.reddit::after { + background-image: url('data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20height%3D%2248%22%20width%3D%2248%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22%23424242%22%3E%3Cpath%20d%3D%22M1.773%2011.76q0-.48.18-.9.18-.418.5-.758t.738-.52q.42-.18.94-.18.88%200%201.518.6%201.2-.76%202.757-1.218%201.558-.46%203.315-.5l1.638-4.673%203.995.96q.2-.52.68-.86.48-.34%201.078-.34.8%200%201.38.56.58.56.58%201.358%200%20.8-.58%201.36-.58.558-1.36.558-.778%200-1.357-.56-.58-.56-.58-1.358l-3.355-.8-1.36%203.797q1.72.08%203.197.54%201.48.458%202.637%201.178.68-.6%201.558-.6.48%200%20.92.18.438.18.758.52t.5.758q.18.42.18.9%200%20.64-.3%201.16-.3.518-.82.838.08.36.08.72%200%201.237-.72%202.376-.718%201.138-1.976%201.977-1.26.84-2.936%201.32-1.678.48-3.596.48-1.878%200-3.555-.48-1.678-.48-2.937-1.32-1.258-.838-1.977-1.976-.718-1.14-.718-2.377%200-.2.04-.4%200-.2.04-.36-.48-.32-.78-.818-.3-.5-.3-1.14zm1.758%202.717q0%201.118.66%202.097.66.98%201.798%201.718%201.138.74%202.696%201.16%201.558.418%203.296.418%201.738%200%203.276-.42%201.538-.42%202.697-1.158%201.158-.74%201.817-1.718.66-.98.66-2.097%200-1.12-.66-2.117-.66-1-1.817-1.738-1.16-.74-2.697-1.16-1.538-.418-3.276-.418-1.738%200-3.296.42-1.558.42-2.696%201.158-1.14.74-1.798%201.738-.66.998-.66%202.117zm4.076-1.1q0-.578.44-1.017.438-.44%201.038-.44.6%200%201.018.44.42.44.42%201.018%200%20.58-.42%201-.42.418-1.018.418-.6%200-1.04-.42-.438-.418-.438-.998zm1.078%203.377q.24-.2.68.16.12.12.26.2.14.08.3.14.158.06.258.1.1.04.28.08.18.04.26.06.08.02.3.06.22.04.28.04.06%200%20.318.02.26.02.34.02%201.558-.08%202.717-.72.44-.4.72-.16.198.36-.24.72-1.2.918-3.197.918-2.157-.04-3.116-.92-.52-.398-.16-.718zm4.954-3.396q0-.6.42-1.018.42-.42%201.038-.42.62%200%201.04.42.418.42.418%201.018%200%20.6-.42%201.02-.42.418-1.038.418-.62%200-1.04-.42-.418-.418-.418-1.018z%22%2F%3E%3Cpath%20d%3D%22M0%200h24v24H0z%22%20fill%3D%22none%22%2F%3E%3C%2Fsvg%3E'); +} + +.provider.twitch { + background: #6441a5; +} + +.provider.twitch::after { + background-image: url('data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2248%22%20height%3D%2248%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22%23fff%22%3E%3Cpath%20d%3D%22M4%202h18v12l-5%205h-4l-3%203H7v-3H2V6l2-4m16%2011V4H6v12h3v3l3-3h5l3-3m-5-6h2v5h-2V7m-3%200v5h-2V7h2z%22%2F%3E%3Cpath%20d%3D%22M0%200h24v24H0z%22%20fill%3D%22none%22%2F%3E%3C%2Fsvg%3E'); +} + +.provider.twitter { + background: #55acee; +} + +.provider.twitter::after { + background-image: url('data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2248%22%20height%3D%2248%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22%23fff%22%3E%3Cpath%20d%3D%22M22.46%206c-.77.35-1.6.58-2.46.69.88-.53%201.56-1.37%201.88-2.38-.83.5-1.75.85-2.72%201.05C18.37%204.5%2017.26%204%2016%204c-2.35%200-4.27%201.92-4.27%204.29%200%20.34.04.67.11.98C8.28%209.09%205.11%207.38%203%204.79c-.37.63-.58%201.37-.58%202.15%200%201.49.75%202.81%201.91%203.56-.71%200-1.37-.2-1.95-.5v.03c0%202.08%201.48%203.82%203.44%204.21-.36.1-.74.15-1.13.15-.27%200-.54-.03-.8-.08.54%201.69%202.11%202.95%204%202.98-1.46%201.16-3.31%201.84-5.33%201.84-.34%200-.68-.02-1.02-.06C3.44%2020.29%205.7%2021%208.12%2021%2016%2021%2020.33%2014.46%2020.33%208.79c0-.19%200-.37-.01-.56.84-.6%201.56-1.36%202.14-2.23z%22%2F%3E%3Cpath%20d%3D%22M0%200h24v24H0z%22%20fill%3D%22none%22%2F%3E%3C%2Fsvg%3E'); +} + +.provider.github, .provider.codepen { + background: #000; +} + +.provider.github::before, .provider.codepen::before { + background: #fff; +} + +.provider.github::after { + background-image: url('data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2248%22%20height%3D%2248%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22%23fff%22%3E%3Cpath%20d%3D%22M12%202A10%2010%200%200%200%202%2012c0%204.42%202.87%208.17%206.84%209.5.5.08.66-.23.66-.5v-1.69c-2.77.6-3.36-1.34-3.36-1.34-.46-1.16-1.11-1.47-1.11-1.47-.91-.62.07-.6.07-.6%201%20.07%201.53%201.03%201.53%201.03.87%201.52%202.34%201.07%202.91.83.09-.65.35-1.09.63-1.34-2.22-.25-4.55-1.11-4.55-4.92%200-1.11.38-2%201.03-2.71-.1-.25-.45-1.29.1-2.64%200%200%20.84-.27%202.75%201.02.79-.22%201.65-.33%202.5-.33.85%200%201.71.11%202.5.33%201.91-1.29%202.75-1.02%202.75-1.02.55%201.35.2%202.39.1%202.64.65.71%201.03%201.6%201.03%202.71%200%203.82-2.34%204.66-4.57%204.91.36.31.69.92.69%201.85V21c0%20.27.16.59.67.5C19.14%2020.16%2022%2016.42%2022%2012A10%2010%200%200%200%2012%202z%22%2F%3E%3Cpath%20d%3D%22M0%200h24v24H0z%22%20fill%3D%22none%22%2F%3E%3C%2Fsvg%3E'); +} + +.provider.codepen::after { + background-image: url('data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2248%22%20height%3D%2248%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22%23fff%22%3E%3Cpath%20d%3D%22M19.45%2013.29L17.5%2012l1.95-1.29m-6.68%208.07v-3.61l3.36-2.24%202.7%201.81M12%2013.83L9.26%2012%2012%2010.17%2014.74%2012m-3.51%206.78l-6.06-4.04%202.7-1.81%203.36%202.24m-6.68-4.46L6.5%2012l-1.95%201.29m6.68-8.07v3.61l-3.36%202.24-2.7-1.81m7.6-4.04l6.06%204.04-2.7%201.81-3.36-2.24m8.23.33v-.04c0-.02%200-.04-.03-.06%200-.01%200-.03-.01-.06%200%200-.01%200-.02-.04%200-.01-.01-.02-.02-.03%200-.02-.01-.04-.02-.05-.01-.02-.02-.03-.02-.04-.01-.02-.03-.03-.04-.05-.01-.01-.01-.02-.02-.03a.04.04%200%200%200-.04-.04l-.03-.03c-.02-.02-.03-.03-.05-.04l-.03-.03c-.01%200-.01%200-.01-.01l-8.23-5.48c-.26-.17-.6-.17-.86%200L3.34%208.61c0%20.01%200%20.01-.01.01l-.03.03c-.02.01-.03.02-.05.04l-.03.03-.04.04c-.01.01-.01.02-.02.03-.01.02-.03.03-.04.05%200%20.01-.01.02-.02.04-.01.01-.02.03-.02.05-.01.01-.02.02-.02.03-.01.04-.01.04-.02.04-.01.03-.01.05-.01.06C3%209.08%203%209.1%203%209.12v5.76c0%20.02%200%20.04.03.06%200%20.01%200%20.03.01.06.01%200%20.01%200%20.02.04%200%20.01.01.02.02.03%200%20.02.01.04.02.05.01.02.02.03.02.04.01.02.03.03.04.05.01.01.01.02.02.03.02.01.03.03.04.04l.03.03c.02.02.03.03.05.04l.03.03c.01%200%20.01%200%20.01.01l8.23%205.48c.13.09.28.13.43.13.15%200%20.3-.04.43-.13l8.23-5.48c0-.01%200-.01.01-.01l.03-.03c.02-.01.03-.02.05-.04l.03-.03c.01-.01.02-.03.04-.04.01-.01.01-.02.02-.03.01-.02.03-.03.04-.05%200-.01.01-.02.02-.04.01-.01.02-.03.02-.05.01-.01.02-.02.02-.03.01-.04.02-.04.02-.04.01-.03.01-.05.01-.06.03-.02.03-.04.03-.06v-.04-5.58-.1z%22%2F%3E%3Cpath%20d%3D%22M0%200h24v24H0z%22%20fill%3D%22none%22%2F%3E%3C%2Fsvg%3E'); +} + +.provider.dribbble { + background: #ea4c89; +} + +.provider.dribbble::after { + background-image: url('data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2248%22%20height%3D%2248%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22%23fff%22%3E%3Cpath%20d%3D%22M16.42%2018.42C16%2016.5%2015.5%2014.73%2015%2013.17c.5-.07%201-.11%201.58-.11h.02c.93%200%201.95.12%203.06.37-.38%202.07-1.58%203.84-3.24%204.99M12%2019.8c-1.74%200-3.34-.57-4.64-1.54.28-.45.87-1.32%201.82-2.22.96-.93%202.32-1.89%204.05-2.46.59%201.67%201.13%203.57%201.54%205.71-.86.33-1.77.51-2.77.51M4.2%2012v-.11c.22.01.51.01.85.01h.01c1.56-.01%204.3-.14%207.08-1.01.15.33.3.67.45%201.03-1.86.62-3.32%201.61-4.4%202.58-1.03.96-1.74%201.89-2.15%202.5-1.14-1.34-1.84-3.09-1.84-5m4.35-7c.55.65%201.63%202.06%202.79%204.25-2.34.71-4.73.87-6.16.87h-.13c-.24%200-.45%200-.62-.01C5%207.87%206.5%206%208.55%205M12%204.2c1.84%200%203.53.64%204.86%201.71C15.84%207.14%2014.5%208%2013.03%208.65%2012%206.67%2011%205.25%2010.34%204.38c.54-.11%201.09-.18%201.66-.18m6.13%202.98c.97%201.24%201.58%202.78%201.66%204.45-1.13-.24-2.19-.35-3.19-.35h-.01c-.8%200-1.55.07-2.26.19-.17-.42-.33-.82-.52-1.21%201.58-.69%203.09-1.68%204.32-3.08M12%202A10%2010%200%200%200%202%2012a10%2010%200%200%200%2010%2010%2010%2010%200%200%200%2010-10A10%2010%200%200%200%2012%202z%22%2F%3E%3Cpath%20d%3D%22M0%200h24v24H0z%22%20fill%3D%22none%22%2F%3E%3C%2Fsvg%3E'); +} + +.provider.instagram { + background: #517fa4; +} + +.provider.instagram::after { + background-image: url('data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2248%22%20height%3D%2248%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22%23fff%22%3E%3Cpath%20d%3D%22M20%206.5a.5.5%200%200%201-.5.5h-2a.5.5%200%200%201-.5-.5v-2a.5.5%200%200%201%20.5-.5h2a.5.5%200%200%201%20.5.5M4.5%2020a.5.5%200%200%201-.5-.5V11h2.09c-.06.32-.09.66-.09%201a6%206%200%200%200%206%206%206%206%200%200%200%206-6c0-.34-.04-.68-.09-1H20v8.5a.5.5%200%200%201-.5.5M12%208a4%204%200%200%201%204%204%204%204%200%200%201-4%204%204%204%200%200%201-4-4%204%204%200%200%201%204-4m8-6H4c-1.11%200-2%20.89-2%202v16a2%202%200%200%200%202%202h16a2%202%200%200%200%202-2V4c0-1.11-.9-2-2-2z%22%2F%3E%3Cpath%20d%3D%22M0%200h24v24H0z%22%20fill%3D%22none%22%2F%3E%3C%2Fsvg%3E'); +} + +.provider.soundcloud { + background: #f50; +} + +.provider.soundcloud::after { + background-image: url('data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2248%22%20height%3D%2248%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22%23fff%22%3E%3Cpath%20d%3D%22M11.56%208.87V17h8.76c1.85-.13%202.68-1.27%202.68-2.67%200-1.48-1.12-2.67-2.62-2.67-.38%200-.7.08-1.03.22-.24-2.34-2.23-4.17-4.68-4.17-1.17%200-2.28.44-3.11%201.16m-.88%201.02c-.3-.18-.62-.32-.97-.39V17h1.39V9.34c-.15.16-.29.36-.42.55m-2.35-.54V17h.92V9.38c-.19-.03-.38-.04-.58-.04-.12%200-.23%200-.34.01M6.5%2010v7h.91V9.54c-.33.11-.64.27-.91.46m-1.67%202.5c-.06%200-.12-.06-.19-.09V17h.92v-6.14c-.37.48-.62%201.05-.73%201.64m-2.04-.28v4.69c.21.06.45.09.71.09h.22v-4.86c-.08-.01-.16-.02-.22-.02-.26%200-.5.04-.71.1M1%2014.56c0%20.75.34%201.41.87%201.86v-3.71c-.53.44-.87%201.11-.87%201.85z%22%2F%3E%3Cpath%20d%3D%22M0%200h24v24H0z%22%20fill%3D%22none%22%2F%3E%3C%2Fsvg%3E'); +} + +.provider.wordpress { + background: #21759b; +} + +.provider.wordpress::after { + background-image: url('data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2248%22%20height%3D%2248%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22%23fff%22%3E%3Cpath%20d%3D%22M12.2%2015.5l-2.55%206.22c.75.18%201.54.28%202.35.28.84%200%201.66-.1%202.44-.3m6.17-14.64c.19.9.15%201.99-.22%203.19C19.42%2013.37%2017%2019%2016.1%2021.13c3.48-1.55%205.9-5.01%205.9-9.03%200-1.84-.5-3.57-1.39-5.04M4.31%208.64S3.82%208%203.31%208h-.53C2.28%209.13%202%2010.62%202%2012c0%204.09%202.5%207.61%206.12%209.11M3.13%207.14C4.8%204.03%208.14%202%2012%202c2.5%200%204.78%201.06%206.53%202.56-.5-.1-1.03.01-1.6.33-1.29.74-1.71%202.82-.04%203.87%201.05.65%201.42%202.28%201.38%203.28-.03.99-2.42%205.57-2.42%205.57L13.5%209.63s-.06-.56-.06-.72c0-.2.06-.45.19-.6.09-.09.22-.31.37-.31h1.11v-.86h-6V8h.19c.2%200%20.39.29.57.47.22.23.5%201.08.83%201.96l.87%202.87-1.88%204.33-2.06-8.66s.06-.6.19-.7C7.9%208.2%208%208%208.17%208h.05v-.86H3.13z%22%2F%3E%3Cpath%20d%3D%22M0%200h24v24H0z%22%20fill%3D%22none%22%2F%3E%3C%2Fsvg%3E'); +} + +.provider.protocol-ActiveDirectory { + background: #0078D7; +} + +.provider.protocol-ActiveDirectory::after { + background-image: url('data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2248%22%20height%3D%2248%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22%23fff%22%3E%3Cpath%20d%3D%22M3%2012V6.75l6-1.32v6.48L3%2012m17-9v8.75l-10%20.15V5.21L20%203M3%2013l6%20.09v6.81l-6-1.15V13m17%20.25V22l-10-1.91V13.1l10%20.15z%22%2F%3E%3Cpath%20d%3D%22M0%200h24v24H0z%22%20fill%3D%22none%22%2F%3E%3C%2Fsvg%3E'); +} diff --git a/routes/connect.js b/routes/connect.js index 49bd5dbe..03fa7efa 100644 --- a/routes/connect.js +++ b/routes/connect.js @@ -7,6 +7,7 @@ var settings = require('../boot/settings') , passport = require('passport') , crypto = require('crypto') , qs = require('qs') + , NotFoundError = require('../errors/NotFoundError') ; diff --git a/routes/signin.js b/routes/signin.js index 9a4e06b2..97287f59 100644 --- a/routes/signin.js +++ b/routes/signin.js @@ -7,9 +7,16 @@ var oidc = require('../oidc') , passport = require('passport') , crypto = require('crypto') , qs = require('qs') - , PasswordsDisabledError = require('../errors/PasswordsDisabledError') + , InvalidRequestError = require('../errors/InvalidRequestError') + , providers = require('../providers') ; +var providerInfo = {}; +var providerNames = Object.keys(providers); +for (var i = 0; i < providerNames.length; i++) { + providerInfo[providerNames[i]] = providers[providerNames[i]]; +} + /** * Signin Endpoint @@ -29,7 +36,8 @@ module.exports = function (server) { res.render('signin', { params: qs.stringify(req.query), request: req.query, - providers: settings.providers + providers: settings.providers, + providerInfo: providerInfo }); }); @@ -43,21 +51,34 @@ module.exports = function (server) { oidc.validateAuthorizationParams, oidc.verifyClient, function (req, res, next) { - passport.authenticate('local', function (err, user, info) { - if (!user) { - res.render('signin', { - params: qs.stringify(req.body), - request: req.body, - providers: settings.providers, - error: info.message - }); - } else { - req.login(user, function (err) { - req.session.opbs = crypto.randomBytes(256).toString('hex'); - next(err); - }); - } - })(req, res, next); + if (!settings.providers[req.body.provider]) { + next(new InvalidRequestError()); + } else { + passport.authenticate(req.body.provider, function (err, user, info) { + if (err) { + res.render('signin', { + params: qs.stringify(req.body), + request: req.body, + providers: settings.providers, + providerInfo: providerInfo, + error: err.message + }); + } else if (!user) { + res.render('signin', { + params: qs.stringify(req.body), + request: req.body, + providers: settings.providers, + providerInfo: providerInfo, + formError: info.message + }); + } else { + req.login(user, function (err) { + req.session.opbs = crypto.randomBytes(256).toString('hex'); + next(err); + }); + } + })(req, res, next); + } }, oidc.determineUserScope, oidc.promptToAuthorize, @@ -65,19 +86,20 @@ module.exports = function (server) { ]; if (oidc.beforeAuthorize) { - handler.splice(handler.length - 1, 0, oidc.beforeAuthorize) + handler.splice(handler.length - 1, 0, oidc.beforeAuthorize); } + server.post('/signin', handler); // Only register the password signin post handler // if the password protocol is enabled. - if (settings.providers.password === true) { - server.post('/signin', handler); - } else { - server.post('/signin', function (req, res, next) { - next(new PasswordsDisabledError()); - }); - } + // if (settings.providers.password === true) { + // server.post('/signin', handler); + // } else { + // server.post('/signin', function (req, res, next) { + // next(new PasswordsDisabledError()); + // }); + // } }; diff --git a/views/signin.jade b/views/signin.jade index fd418867..60e895db 100644 --- a/views/signin.jade +++ b/views/signin.jade @@ -4,25 +4,24 @@ html(lang="en") title | Sign in to Anvil Connect - link(rel='stylesheet', href='//maxcdn.bootstrapcdn.com/font-awesome/4.1.0/css/font-awesome.min.css') link(rel='stylesheet', href='/stylesheets/app.css') + link(rel='stylesheet', href='/stylesheets/providers.css') link(rel='stylesheet', href='//fonts.googleapis.com/css?family=Roboto:400,100,100italic,400italic,700,700italic|Raleway:400,100,600,300') body - - .anvilform + .anvilform.blur-transition img.logomark(src='/images/anvil.svg') h1.logotype Anvil span.thin Connect - form.panel(method='POST', action='/signin') + form.panel(method='POST') // adding a class of error to this paragraph will add error styling if error == 'email must be unique' - p + p.error | A user with the same email address has | already registered using a different provider. | Try one of these: - else - p= error + else if error + p.error= error input(type='hidden', name='response_type', value=request.response_type) input(type='hidden', name='client_id', value=request.client_id) input(type='hidden', name='redirect_uri', value=request.redirect_uri) @@ -31,18 +30,105 @@ html(lang="en") input(type='hidden', name='state', value=request.state) input(type='hidden', name='nonce', value=request.nonce) hr.textsep.signinwith - ul.providers - each provider in Object.keys(providers) - if provider !== 'password' - li - a(href='/connect/' + provider + '?' + params) - i(class='provider fa-2x fa fa-' + provider + ' ' + provider) - hr.textsep.or - input(type='text', name='email', placeholder='Email') - input(type='password', name='password', placeholder='Password') - button.callout.full(type='submit', value='Sign in') - | Sign in - p - | Don't have an account? - a(href='/signup?' + params) - | Register in Seconds + - var formBasedProviders = []; + - var formFieldData = {}; + - var providerNames = {}; + - var externalProviders = []; + - var providerIDs = Object.keys(providers); + if providerIDs.length + ul.providers + each provider in providerIDs + - providerNames[provider] = providerInfo[provider].name; + if !providerInfo[provider].fields + - externalProviders.push(provider); + li + a(href='/connect/' + provider + '?' + params, title=providerInfo[provider].name, class='provider protocol-' + providerInfo[provider].protocol + ' '+ provider) + else + - formBasedProviders.push(provider); + - formFieldData[provider] = providerInfo[provider].fields; + if formBasedProviders.length > 0 + - var selectedProvider = (request && request.provider) || formBasedProviders[0]; + if externalProviders.length + hr.textsep.or + .form-provider-name= providerInfo[selectedProvider].name + if formBasedProviders.length > 1 + ul.providers.form-providers + each provider in formBasedProviders + li + a(href='javascript:void(0)', title=providerInfo[provider].name, data-provider=provider, class='provider protocol-' + providerInfo[provider].protocol + ' ' + provider + (provider === selectedProvider ? ' selected-provider' : '')) + input(type='hidden', name='provider', value=selectedProvider) + if formError + p.error= formError + .form-fields + each field in providerInfo[selectedProvider].fields + input(type=field.type, name=field.name, placeholder=field.placeholder || (field.name.charAt(0).toUpperCase() + field.name.substring(1))) + //-input(type='text', name='email', placeholder='Email') + //-input(type='password', name='password', placeholder='Password') + input.callout.full(type='submit', value='Sign in') + if ~providerIDs.indexOf('password') + p.register-message + | Don't have an account? + a(href='/signup?' + params) + | Register in Seconds + .screen(hidden) + .loader + svg.circular + circle.path(cx=28, cy=28, r=20, fill='none', stroke-width=5, stroke-miterlimit=10) + if formBasedProviders.length > 1 + script. + (function() { + var formFieldData = !{JSON.stringify(formFieldData)}; + var providerIDs = !{JSON.stringify(providerNames)}; + var providerTag = document.querySelector('input[name="provider"]'); + var formProviderNametag = document.querySelector('.form-provider-name'); + var formFieldContainer = document.querySelector('.form-fields'); + var formProviderLinks = document.querySelectorAll('.form-providers a'); + var registerMessage = document.querySelector('.register-message'); + var screenElem = document.querySelector('.screen'); + var form = document.querySelector('form'); + var formWrapper = document.querySelector('.anvilform'); + + form.addEventListener('submit', function() { + formWrapper.classList.add('blur'); + screenElem.removeAttribute('hidden'); + }); + + function changeProvider() { + formFieldContainer.innerHTML = ''; + formFieldContainer.appendChild(this.providerFields.cloneNode(true)); + for (var i = 0; i < formProviderLinks.length; i++) { + if (formProviderLinks[i].provider !== this.provider) { + formProviderLinks[i].classList.remove('selected-provider'); + } + } + formProviderNametag.textContent = providerIDs[this.provider]; + providerTag.value = this.provider; + if (registerMessage) { + registerMessage.style.display = this.provider === 'password' ? '' : 'none'; + } + this.classList.add('selected-provider'); + } + + var fragment, fields, input; + for (var i = 0; i < formProviderLinks.length; i++) { + // We could use dataset, but unfortunately there is the dreaded IE10 + formProviderLinks[i].provider = formProviderLinks[i].getAttribute('data-provider'); + fragment = document.createDocumentFragment(); + fields = formFieldData[formProviderLinks[i].provider]; + for (var j = 0; j < fields.length; j++) { + input = document.createElement('input'); + input.name = fields[j].name + if (fields[j].type) { + input.type = fields[j].type; + } + if (fields[j].placeholder) { + input.placeholder = fields[j].placeholder; + } else { + input.placeholder = fields[j].name.charAt(0).toUpperCase() + fields[j].name.substring(1); + } + fragment.appendChild(input); + } + formProviderLinks[i].providerFields = fragment; + formProviderLinks[i].addEventListener('click', changeProvider); + } + })(); diff --git a/views/signup.jade b/views/signup.jade index 98430e05..c4f26176 100644 --- a/views/signup.jade +++ b/views/signup.jade @@ -23,21 +23,11 @@ html(lang="en") input(type='hidden', name='scope', value=request.scope) input(type='hidden', name='state', value=request.state) input(type='hidden', name='nonce', value=request.nonce) - hr.textsep.signupwith - ul.providers - each provider in Object.keys(providers) - if provider !== 'password' - li - a(href='/connect/' + provider + '?' + params) - i(class='provider fa-2x fa fa-' + provider + ' ' + provider) - hr.textsep.or + hr.textsep.signup input(type='text', name='givenName', placeholder='First Name') input(type='text', name='familyName', placeholder='Last Name') input(type='text', name='email', placeholder='Email') input(type='password', name='password', placeholder='Password') - button.callout.full(type='submit', value='Sign up') - | Sign up - - p - | Already have an account? + input.callout.full(type='submit', value='Sign up') + p Already have an account?  a(href='/signin?' + params) Sign in here