Skip to content
This repository was archived by the owner on Aug 1, 2023. It is now read-only.

Commit e688699

Browse files
author
Christopher Schinnerl
authored
Merge pull request #12 from SkynetLabs/karol/sky-30-redirect-auth-error-to-login-page
Custom error page for 401 Unauthorized that redirect to login screen
2 parents 672ec7f + a2fac04 commit e688699

File tree

7 files changed

+137
-13
lines changed

7 files changed

+137
-13
lines changed

nginx/conf.d/include/location-hns

+14-6
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,15 @@ rewrite_by_lua_block {
1313
-- 10.10.10.50 points to handshake-api service (alias not available when using resty-http)
1414
local hnsres_res, hnsres_err = httpc:request_uri("http://10.10.10.50:3100/hnsres/" .. ngx.var.hns_domain)
1515

16-
-- print error and exit with 500 or exit with response if status is not 200
16+
-- exit with 500 or exit with response if status is not 200
1717
if hnsres_err or (hnsres_res and hnsres_res.status ~= ngx.HTTP_OK) then
1818
ngx.status = (hnsres_err and ngx.HTTP_INTERNAL_SERVER_ERROR) or hnsres_res.status
19-
ngx.header["content-type"] = "text/plain"
20-
ngx.say(hnsres_err or hnsres_res.body)
19+
-- do not send response on unauthorized error (status code 401)
20+
-- otherwise we will not be able to display custom error page
21+
if ngx.status != ngx.HTTP_UNAUTHORIZED then
22+
ngx.header["content-type"] = "text/plain"
23+
ngx.say(hnsres_err or hnsres_res.body)
24+
end
2125
return ngx.exit(ngx.status)
2226
end
2327

@@ -46,11 +50,15 @@ rewrite_by_lua_block {
4650
headers = { ["User-Agent"] = "Sia-Agent" }
4751
})
4852

49-
-- print error and exit with 500 or exit with response if status is not 200
53+
-- exit with 500 or exit with response if status is not 200
5054
if registry_err or (registry_res and registry_res.status ~= ngx.HTTP_OK) then
5155
ngx.status = (registry_err and ngx.HTTP_INTERNAL_SERVER_ERROR) or registry_res.status
52-
ngx.header["content-type"] = "text/plain"
53-
ngx.say(registry_err or registry_res.body)
56+
-- do not send response on unauthorized error (status code 401)
57+
-- otherwise we will not be able to display custom error page
58+
if ngx.status != ngx.HTTP_UNAUTHORIZED then
59+
ngx.header["content-type"] = "text/plain"
60+
ngx.say(registry_err or registry_res.body)
61+
end
5462
return ngx.exit(ngx.status)
5563
end
5664

nginx/conf.d/include/location-skylink

+1
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ proxy_read_timeout 600;
3636
proxy_set_header User-Agent: Sia-Agent;
3737

3838
proxy_pass http://sia:9980/skynet/skylink/$skylink$path$is_args$args;
39+
proxy_intercept_errors on;
3940

4041
log_by_lua_block {
4142
local skynet_account = require("skynet.account")

nginx/conf.d/server/server.api

+6
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,12 @@ rewrite ^/stats /skynet/stats permanent;
2323
rewrite ^/skynet/blacklist /skynet/blocklist permanent;
2424
rewrite ^/docs(?:/(.*))?$ https://sdk.skynetlabs.com/$1 permanent;
2525

26+
error_page 401 /401.html;
27+
location = /401.html {
28+
root /etc/nginx/conf.d/pages;
29+
internal;
30+
}
31+
2632
location / {
2733
include /etc/nginx/conf.d/include/cors;
2834

nginx/conf.d/server/server.dnslink

+6-2
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,12 @@ location / {
2424
ngx.var.path = match_skylink[2] or "/"
2525
else
2626
ngx.status = (err and ngx.HTTP_INTERNAL_SERVER_ERROR) or res.status
27-
ngx.header["content-type"] = "text/plain"
28-
ngx.say(err or res.body)
27+
-- do not send response on unauthorized error (status code 401)
28+
-- otherwise we will not be able to display custom error page
29+
if ngx.status != ngx.HTTP_UNAUTHORIZED then
30+
ngx.header["content-type"] = "text/plain"
31+
ngx.say(err or res.body)
32+
end
2933
ngx.exit(ngx.status)
3034
end
3135
else

nginx/libs/skynet/account.lua

+2-5
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,8 @@ function _M.get_auth_headers()
4343
end
4444

4545
-- handle request exit when access to portal should be restricted to authenticated users only
46-
function _M.exit_access_unauthorized(message)
47-
ngx.status = ngx.HTTP_UNAUTHORIZED
48-
ngx.header["content-type"] = "text/plain"
49-
ngx.say(message or "Portal operator restricted access to authenticated users only")
50-
return ngx.exit(ngx.status)
46+
function _M.exit_access_unauthorized()
47+
return ngx.exit(ngx.HTTP_UNAUTHORIZED)
5148
end
5249

5350
-- handle request exit when access to portal should be restricted to subscription users only

nginx/libs/skynet/account.spec.lua

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
local skynet_account = require('skynet.account')
2+
3+
describe("exit_access_unauthorized", function()
4+
before_each(function()
5+
stub(ngx, "exit")
6+
end)
7+
8+
after_each(function()
9+
mock.revert(ngx)
10+
end)
11+
12+
it("should call ngx.exit with status code 401", function()
13+
skynet_account.exit_access_unauthorized()
14+
15+
-- expect exit called with ngx.HTTP_UNAUTHORIZED code 401
16+
assert.stub(ngx.exit).was_called_with(401)
17+
end)
18+
end)
+90
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
<!DOCTYPE html>
2+
<html class="h-full">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
6+
<!-- meta tags generated with https://metatags.io/ -->
7+
<title>401: Unauthorized</title>
8+
<meta name="title" content="401: Unauthorized" />
9+
<meta
10+
name="description"
11+
content="You must be authenticated to access this content"
12+
/>
13+
<!-- svg favicon encoded with https://yoksel.github.io/url-encoder/ -->
14+
<link
15+
rel="icon"
16+
href="data:image/svg+xml, %3Csvg role='img' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg' fill='%2300C65E' %3E%3Ctitle%3ESkynet%3C/title%3E%3Cpath d='m-.0004 6.4602 21.3893 11.297c.561.2935.6633 1.0532.1999 1.4846h-.011a10.0399 10.0399 0 0 1-2.2335 1.5307c-6.912 3.4734-14.9917-1.838-14.5438-9.5605l2.8601 1.9752c.856 4.508 5.6187 7.1094 9.8742 5.3932zm8.6477 3.1509 14.3661 5.6785a.8704.8704 0 0 1 .5197 1.0466v.0182c-.1537.5377-.7668.7938-1.2575.5252zm5.2896-7.4375c2.7093-.2325 6.0946.7869 8.1116 3.3871 1.699 2.1951 2.0497 4.8772 1.9298 7.6465v-.007c-.0478.5874-.6494.9616-1.1975.745l-9.7652-3.8596 9.0656 2.4313a7.296 7.296 0 0 0-1.0677-4.5631c-2.9683-4.7678-9.9847-4.5344-12.6297.4201a7.5048 7.5048 0 0 0-.398.8831L5.5546 7.9614c.069-.1017.1417-.198.2144-.2962.1163-.2416.2417-.487.3798-.7268 1.6118-2.7911 4.3102-4.4338 7.1558-4.6973.2108-.0182.4215-.049.6323-.0672z' /%3E%3C/svg%3E"
17+
type="image/svg+xml"
18+
/>
19+
<!-- https://fonts.google.com/specimen/Sora -->
20+
<link rel="preconnect" href="https://fonts.googleapis.com" />
21+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
22+
<link
23+
href="https://fonts.googleapis.com/css2?family=Sora:wght@400;800&display=swap"
24+
rel="stylesheet"
25+
/>
26+
<!-- https://tailwindcss.com/docs/installation/play-cdn -->
27+
<script src="https://cdn.tailwindcss.com"></script>
28+
<script>
29+
tailwind.config = {
30+
theme: { extend: { fontFamily: { sans: ["Sora", "sans-serif"] } } },
31+
};
32+
</script>
33+
</head>
34+
<body class="h-full">
35+
<div
36+
class="bg-white min-h-full px-4 py-16 sm:px-6 sm:py-24 md:grid md:place-items-center lg:px-8"
37+
>
38+
<div class="max-w-max mx-auto">
39+
<main class="sm:flex">
40+
<svg
41+
role="img"
42+
viewBox="0 0 24 24"
43+
xmlns="http://www.w3.org/2000/svg"
44+
height="88"
45+
fill="#00C65E"
46+
>
47+
<title>Skynet</title>
48+
<path
49+
d="m-.0004 6.4602 21.3893 11.297c.561.2935.6633 1.0532.1999 1.4846h-.011a10.0399 10.0399 0 0 1-2.2335 1.5307c-6.912 3.4734-14.9917-1.838-14.5438-9.5605l2.8601 1.9752c.856 4.508 5.6187 7.1094 9.8742 5.3932zm8.6477 3.1509 14.3661 5.6785a.8704.8704 0 0 1 .5197 1.0466v.0182c-.1537.5377-.7668.7938-1.2575.5252zm5.2896-7.4375c2.7093-.2325 6.0946.7869 8.1116 3.3871 1.699 2.1951 2.0497 4.8772 1.9298 7.6465v-.007c-.0478.5874-.6494.9616-1.1975.745l-9.7652-3.8596 9.0656 2.4313a7.296 7.296 0 0 0-1.0677-4.5631c-2.9683-4.7678-9.9847-4.5344-12.6297.4201a7.5048 7.5048 0 0 0-.398.8831L5.5546 7.9614c.069-.1017.1417-.198.2144-.2962.1163-.2416.2417-.487.3798-.7268 1.6118-2.7911 4.3102-4.4338 7.1558-4.6973.2108-.0182.4215-.049.6323-.0672z"
50+
/>
51+
</svg>
52+
<div class="sm:ml-6">
53+
<div class="sm:border-l sm:border-gray-200 sm:pl-6">
54+
<h1
55+
class="text-xl font-extrabold text-gray-900 tracking-tight sm:text-2xl"
56+
>
57+
You must be authenticated to access this content
58+
</h1>
59+
<p class="mt-1 text-base text-gray-500">
60+
You're being redirected to the Log In page of this Skynet Portal
61+
</p>
62+
<p class="mt-2 text-sm text-gray-300">HTTP 401: Unauthorized</p>
63+
</div>
64+
<div
65+
class="mt-10 flex space-x-3 sm:border-l sm:border-transparent sm:pl-6"
66+
>
67+
<p class="mt-1 text-sm text-gray-500">
68+
If you're not redirected automatically,
69+
<a
70+
href="https://account.${PORTAL_DOMAIN}/auth/login"
71+
class="text-[#00C65E]"
72+
>click here</a
73+
>.
74+
</p>
75+
</div>
76+
</div>
77+
</main>
78+
</div>
79+
</div>
80+
<script>
81+
// prevent auto redirect if not on portal domain to allow local development
82+
if (window.location.hostname.endsWith("${PORTAL_DOMAIN}")) {
83+
setTimeout(function redirect() {
84+
const encodedReturnTo = encodeURIComponent(window.location.href);
85+
window.location.href = `https://account.${PORTAL_DOMAIN}/auth/login?return_to=${encodedReturnTo}`;
86+
}, 5000); // redirect after 5 seconds
87+
}
88+
</script>
89+
</body>
90+
</html>

0 commit comments

Comments
 (0)