Для приготовления авторизации через auth0 нам понадобится сам nginx и его плагины encrypted-session, headers-more, auth_request, set-misc, echo, json, evaluate и jwt. Можно также воспользоваться готовым образом.
Для начала, получим публичный ключ командой
curl https://our.application.domain.com/pem | openssl x509 -pubkey -noout > pub.key
Далее, в конфиге
# отключаем авторизационный заголовок
more_clear_input_headers Authorization;
# задаём домен нашего приложения на auth0
set $domain our.application.domain.com;
# задаём и кодируем идентификатор нашего приложения на auth0
set_escape_uri $client_id_escape YourApplicationClientID;
# кодируем исходный запрос
set_escape_uri $request_uri_escape $request_uri;
# задаём и кодируем адрес возврата
set_escape_uri $redirect_uri_escape https://$host/login?request_uri=$request_uri_escape;
Теперь защищаем всё авторизацией
auth_request /auth;
location =/auth {
internal; # только для внутреннего использования
# раскодируем авторизационную куку
set_decode_base64 $auth_decode $cookie_auth;
# расшифровываем авторизационную куку
set_decrypt_session $auth_decrypt $auth_decode;
# если не удалось расшифровать, то значит пользователь не авторизован
if ($auth_decrypt = "") { return 401 UNAUTHORIZED; }
# подменяем авторизацию на basic (чтобы использовать переменную $remote_user)
more_set_input_headers "Authorization: Basic $auth_decrypt";
echo -n OK; # пользователь авторизован
}
Для авторизованных пользователей
location / {
alias html/$remote_user/; # показываем контент из их папки
}
А при отсутствии авторизации
error_page 401 = @error;
location @error {
# задаём и кодируем скоупы
set_escape_uri $scope_escape "openid profile";
# перенаправляем клиента на auth0
return 303 https://$domain/authorize?client_id=$client_id_escape&redirect_uri=$redirect_uri_escape&response_type=code&scope=$scope_escape&state=0;
}
После успешной авторизации пользователя в auth0, его перенаправляет на заданный адрес возврата
location =/login {
auth_request off; # не используем авторизацию
evaluate $token /token; # получаем токен
json_loads $token_json $token; # загружаем его
json_dumps $id_token $token_json id_token; # извлекаем идентификатор
auth_jwt $id_token; # задаём его в качестве jwt
auth_jwt_alg RS256; # задаём тип токена
auth_jwt_key /path/to/our/pub.key file; # задаём наш публичный ключ
# и переходим к дальнейшей обработке
try_files /notexistent /continue?name=$jwt_grant_name&request_uri=$arg_request_uri;
}
location =/continue {
auth_request off; # не используем авторизацию
internal; # только для внутреннего использования
# если имя не получено, то значит пользователь не авторизован
if ($arg_name = "") { return 401 UNAUTHORIZED; }
# задаём случайный пароль для basic авторизации
set_secure_random_alphanum $password 8;
# задаём и кодируем basic авторизацию
set_encode_base64 $username_password_encode $arg_name:$password;
# зашифровываем basic авторизацию на 12 часов (12 * 60 * 60 = 43200)
set_encrypt_session $auth_encrypt $username_password_encode 43200;
# кодируем зашифрованную basic авторизацию
set_encode_base64 $auth_encode $auth_encrypt;
# помещаем зашифрованную basic авторизацию в авторизационную куку на 12 часов
add_header Set-Cookie "Auth=$auth_encode; Max-Age=43200";
# копируем запрос из аргумента
set $arg_request_uri_or_slash $arg_request_uri;
# если аргумент не задан, то начало
set_if_empty $arg_request_uri_or_slash /;
# раскодируем запрос
set_unescape_uri $request_uri_unescape $arg_request_uri_or_slash;
# и перенаправляем на сохранённый запрос
return 303 $request_uri_unescape;
}
Токен получаем так
location =/token {
internal; # только для внутреннего использования
# задаём и кодируем пароль нашего приложения на auth0
set_escape_uri $client_secret_escape YourApplicationClientSecret;
# задаём метод запроса
proxy_method POST;
# задаём тело запроса
proxy_set_body client_id=$client_id_escape&code=$arg_code&redirect_uri=$redirect_uri_escape&client_secret=$client_secret_escape&grant_type=authorization_code;
# запрещаем сжимать ответ
proxy_set_header Accept-Encoding deflate;
# задаём тип тела запроса
proxy_set_header Content-Type application/x-www-form-urlencoded;
# разрешаем передавать имя сервера
proxy_ssl_server_name on;
# перенаправляем запрос на auth0
proxy_pass https://$domain/oauth/token;
}
Rive
В переносе бизнес-логики внутрь nginx меня смущает то, что язык разметки conf-файлов декларирует if is evil, таким образом усложнение логики в таких спартанских условиях приведёт к куче бойлерплейта для обхода этих грабель.
RekGRpth Автор
Это просто proof-of-concept, при желании, вместо использования if и указанной кучи плагинов, можно написать один плагин, который внутри будет всё делать.
RekGRpth Автор
Вот, я как раз и использую внутри if только return и всё