-
Notifications
You must be signed in to change notification settings - Fork 30.1k
Commit
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
/*--------------------------------------------------------------------------------------------- | ||
* Copyright (c) Microsoft Corporation. All rights reserved. | ||
* Licensed under the MIT License. See License.txt in the project root for license information. | ||
*--------------------------------------------------------------------------------------------*/ | ||
|
||
html { | ||
height: 100%; | ||
} | ||
|
||
body { | ||
box-sizing: border-box; | ||
min-height: 100%; | ||
margin: 0; | ||
padding: 15px 30px; | ||
display: flex; | ||
flex-direction: column; | ||
color: white; | ||
font-family: "Segoe UI","Helvetica Neue","Helvetica",Arial,sans-serif; | ||
background-color: #373277; | ||
} | ||
|
||
.branding { | ||
background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI+PGRlZnM+PHN0eWxlPi5pY29uLWNhbnZhcy10cmFuc3BhcmVudHtmaWxsOiNmNmY2ZjY7b3BhY2l0eTowO30uaWNvbi13aGl0ZXtmaWxsOiNmZmY7fTwvc3R5bGU+PC9kZWZzPjx0aXRsZT5CcmFuZFZpc3VhbFN0dWRpb0NvZGUyMDE3UlRXXzI0eF93aGl0ZV8yNHg8L3RpdGxlPjxwYXRoIGNsYXNzPSJpY29uLWNhbnZhcy10cmFuc3BhcmVudCIgZD0iTTI0LDBWMjRIMFYwWiIvPjxwYXRoIGNsYXNzPSJpY29uLXdoaXRlIiBkPSJNMjQsMi41VjIxLjVMMTgsMjQsMCwxOC41di0uNTYxbDE4LDEuNTQ1VjBaTTEsMTMuMTExLDQuMzg1LDEwLDEsNi44ODlsMS40MTgtLjgyN0w1Ljg1Myw4LjY1LDEyLDNsMywxLjQ1NlYxNS41NDRMMTIsMTcsNS44NTMsMTEuMzUsMi40MTksMTMuOTM5Wk03LjY0NCwxMCwxMiwxMy4yODNWNi43MTdaIi8+PC9zdmc+"); | ||
background-size: 24px; | ||
background-repeat: no-repeat; | ||
background-position: left 50%; | ||
padding-left: 36px; | ||
font-size: 20px; | ||
letter-spacing: -0.04rem; | ||
font-weight: 400; | ||
color: white; | ||
text-decoration: none; | ||
} | ||
|
||
.message-container { | ||
flex-grow: 1; | ||
display: flex; | ||
align-items: center; | ||
justify-content: center; | ||
margin: 0 30px; | ||
} | ||
|
||
.message { | ||
font-weight: 300; | ||
font-size: 1.3rem; | ||
} | ||
|
||
body.error .message { | ||
display: none; | ||
} | ||
|
||
body.error .error-message { | ||
display: block; | ||
} | ||
|
||
.error-message { | ||
display: none; | ||
font-weight: 300; | ||
font-size: 1.3rem; | ||
} | ||
|
||
.error-text { | ||
color: red; | ||
font-size: 1rem; | ||
} | ||
|
||
@font-face { | ||
font-family: 'Segoe UI'; | ||
src: url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/light/latest.eot"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/light/latest.eot?#iefix") format("embedded-opentype"); | ||
src: local("Segoe UI Light"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/light/latest.woff2") format("woff2"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/light/latest.woff") format("woff"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/light/latest.ttf") format("truetype"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/light/latest.svg#web") format("svg"); | ||
font-weight: 200 | ||
} | ||
|
||
@font-face { | ||
font-family: 'Segoe UI'; | ||
src: url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/semilight/latest.eot"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/semilight/latest.eot?#iefix") format("embedded-opentype"); | ||
src: local("Segoe UI Semilight"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/semilight/latest.woff2") format("woff2"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/semilight/latest.woff") format("woff"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/semilight/latest.ttf") format("truetype"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/semilight/latest.svg#web") format("svg"); | ||
font-weight: 300 | ||
} | ||
|
||
@font-face { | ||
font-family: 'Segoe UI'; | ||
src: url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/normal/latest.eot"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/normal/latest.eot?#iefix") format("embedded-opentype"); | ||
src: local("Segoe UI"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/normal/latest.woff2") format("woff"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/normal/latest.woff") format("woff"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/normal/latest.ttf") format("truetype"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/normal/latest.svg#web") format("svg"); | ||
font-weight: 400 | ||
} | ||
|
||
@font-face { | ||
font-family: 'Segoe UI'; | ||
src: url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/semibold/latest.eot"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/semibold/latest.eot?#iefix") format("embedded-opentype"); | ||
src: local("Segoe UI Semibold"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/semibold/latest.woff2") format("woff"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/semibold/latest.woff") format("woff"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/semibold/latest.ttf") format("truetype"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/semibold/latest.svg#web") format("svg"); | ||
font-weight: 600 | ||
} | ||
|
||
@font-face { | ||
font-family: 'Segoe UI'; | ||
src: url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/bold/latest.eot"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/bold/latest.eot?#iefix") format("embedded-opentype"); | ||
src: local("Segoe UI Bold"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/bold/latest.woff2") format("woff"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/bold/latest.woff") format("woff"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/bold/latest.ttf") format("truetype"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/bold/latest.svg#web") format("svg"); | ||
font-weight: 700 | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
<!-- Copyright (C) Microsoft Corporation. All rights reserved. --> | ||
<!DOCTYPE html> | ||
<html> | ||
<head> | ||
<meta charset="utf-8" /> | ||
<meta http-equiv="X-UA-Compatible" content="IE=edge"> | ||
<title>Azure Account - Sign In</title> | ||
<meta name="viewport" content="width=device-width, initial-scale=1"> | ||
<link rel="stylesheet" type="text/css" media="screen" href="auth.css" /> | ||
</head> | ||
<body> | ||
<a class="branding" href="https://code.visualstudio.com/"> | ||
Visual Studio Code | ||
</a> | ||
<div class="message-container"> | ||
<div class="message"> | ||
You are signed in now and can close this page. | ||
</div> | ||
<div class="error-message"> | ||
An error occurred while signing in: | ||
<div class="error-text"></div> | ||
</div> | ||
</div> | ||
<script> | ||
var search = window.location.search; | ||
var error = (/[?&^]error=([^&]+)/.exec(search) || [])[1]; | ||
if (error) { | ||
document.querySelector('.error-text') | ||
.textContent = decodeURIComponent(error); | ||
document.querySelector('body') | ||
.classList.add('error'); | ||
} | ||
</script> | ||
</body> | ||
</html> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,162 @@ | ||
/*--------------------------------------------------------------------------------------------- | ||
* Copyright (c) Microsoft Corporation. All rights reserved. | ||
* Licensed under the MIT License. See License.txt in the project root for license information. | ||
*--------------------------------------------------------------------------------------------*/ | ||
|
||
import * as http from 'http'; | ||
import * as url from 'url'; | ||
import * as fs from 'fs'; | ||
import * as net from 'net'; | ||
import { getPathFromAmdModule } from 'vs/base/common/amd'; | ||
|
||
interface Deferred<T> { | ||
resolve: (result: T | Promise<T>) => void; | ||
reject: (reason: any) => void; | ||
} | ||
|
||
export function createTerminateServer(server: http.Server) { | ||
const sockets: Record<number, net.Socket> = {}; | ||
let socketCount = 0; | ||
server.on('connection', socket => { | ||
const id = socketCount++; | ||
sockets[id] = socket; | ||
socket.on('close', () => { | ||
delete sockets[id]; | ||
}); | ||
}); | ||
return async () => { | ||
const result = new Promise<void>(resolve => server.close(resolve)); | ||
for (const id in sockets) { | ||
sockets[id].destroy(); | ||
} | ||
return result; | ||
}; | ||
} | ||
|
||
export async function startServer(server: http.Server): Promise<string> { | ||
let portTimer: NodeJS.Timer; | ||
|
||
function cancelPortTimer() { | ||
clearTimeout(portTimer); | ||
} | ||
|
||
const port = new Promise<string>((resolve, reject) => { | ||
portTimer = setTimeout(() => { | ||
reject(new Error('Timeout waiting for port')); | ||
}, 5000); | ||
|
||
server.on('listening', () => { | ||
const address = server.address(); | ||
if (typeof address === 'string') { | ||
resolve(address); | ||
} else { | ||
resolve(address.port.toString()); | ||
} | ||
}); | ||
|
||
server.on('error', err => { | ||
reject(err); | ||
}); | ||
|
||
server.on('close', () => { | ||
reject(new Error('Closed')); | ||
}); | ||
|
||
server.listen(0); | ||
This comment has been minimized.
Sorry, something went wrong.
This comment has been minimized.
Sorry, something went wrong.
TylerLeonhardt
Member
|
||
}); | ||
|
||
port.then(cancelPortTimer, cancelPortTimer); | ||
return port; | ||
} | ||
|
||
function sendFile(res: http.ServerResponse, filepath: string, contentType: string) { | ||
fs.readFile(filepath, (err, body) => { | ||
if (err) { | ||
console.error(err); | ||
} else { | ||
res.writeHead(200, { | ||
'Content-Length': body.length, | ||
'Content-Type': contentType | ||
}); | ||
res.end(body); | ||
} | ||
}); | ||
} | ||
|
||
async function callback(nonce: string, reqUrl: url.Url): Promise<string> { | ||
const query = reqUrl.query; | ||
if (!query || typeof query === 'string') { | ||
throw new Error('No query received.'); | ||
} | ||
|
||
let error = query.error_description || query.error; | ||
|
||
if (!error) { | ||
const state = (query.state as string) || ''; | ||
const receivedNonce = (state.split(',')[1] || '').replace(/ /g, '+'); | ||
if (receivedNonce !== nonce) { | ||
error = 'Nonce does not match.'; | ||
} | ||
} | ||
|
||
const code = query.code as string; | ||
if (!error && code) { | ||
return code; | ||
} | ||
|
||
throw new Error((error as string) || 'No code received.'); | ||
} | ||
|
||
export function createServer(nonce: string) { | ||
type RedirectResult = { req: http.IncomingMessage; res: http.ServerResponse; } | { err: any; res: http.ServerResponse; }; | ||
let deferredRedirect: Deferred<RedirectResult>; | ||
const redirectPromise = new Promise<RedirectResult>((resolve, reject) => deferredRedirect = { resolve, reject }); | ||
|
||
type CodeResult = { code: string; res: http.ServerResponse; } | { err: any; res: http.ServerResponse; }; | ||
let deferredCode: Deferred<CodeResult>; | ||
const codePromise = new Promise<CodeResult>((resolve, reject) => deferredCode = { resolve, reject }); | ||
|
||
const codeTimer = setTimeout(() => { | ||
deferredCode.reject(new Error('Timeout waiting for code')); | ||
}, 5 * 60 * 1000); | ||
|
||
function cancelCodeTimer() { | ||
clearTimeout(codeTimer); | ||
} | ||
|
||
const server = http.createServer(function (req, res) { | ||
const reqUrl = url.parse(req.url!, /* parseQueryString */ true); | ||
switch (reqUrl.pathname) { | ||
case '/signin': | ||
const receivedNonce = ((reqUrl.query.nonce as string) || '').replace(/ /g, '+'); | ||
if (receivedNonce === nonce) { | ||
deferredRedirect.resolve({ req, res }); | ||
} else { | ||
const err = new Error('Nonce does not match.'); | ||
deferredRedirect.resolve({ err, res }); | ||
} | ||
break; | ||
case '/': | ||
sendFile(res, getPathFromAmdModule(require, '../common/auth.html'), 'text/html; charset=utf-8'); | ||
break; | ||
case '/auth.css': | ||
sendFile(res, getPathFromAmdModule(require, '../common/auth.css'), 'text/css; charset=utf-8'); | ||
break; | ||
case '/callback': | ||
deferredCode.resolve(callback(nonce, reqUrl) | ||
.then(code => ({ code, res }), err => ({ err, res }))); | ||
break; | ||
default: | ||
res.writeHead(404); | ||
res.end(); | ||
break; | ||
} | ||
}); | ||
|
||
codePromise.then(cancelCodeTimer, cancelCodeTimer); | ||
return { | ||
server, | ||
redirectPromise, | ||
codePromise | ||
}; | ||
} |
@RMacfarlane Any need to listen on all network interfaces? Can this be limited to just the local network interface (by adding '127.0.0.1' as the second argument)? /cc @TylerLeonhardt