Статья на 1div0.ru
Данный модуль содержит механизм авторизации некоторого веб-приложения с получением данных
о пользователе с помощью протокола OAuth2 через различные сервисы, поддерживающие этот протокол.
В дальнейшем формируются куки с информацией о пользователе и CSRF-токеном.
CSRF-токен простого типа, действует в пределах работы программы на сервере.
Основу всего механизма составляет объект типа OAuthCollect
, который содержит экземпляры
интерфейсов OAuthorizator
. Интерфейс OAuthorizator описывает каждый отдельный
сервис авторизации (Yandex, Google и т. д.). Смысл в том, чтобы получить данные о пользователе (его
идентификатор в сервисе, поддерживающим OAuth, его имя и адрес электронной почты).
Записать их в базу данных и выдать некоторый токен. Пока есть токен - считать, что пользователь
авторизован. Если токена нет - повторно его авторизовывать через OAuth.
OAuthCollect содержит функцию инициализации Init()
. Её нужно вызвать в первую очередь.
Эта функция имеет следующие параметры:
- VerificationCodeCallbackURL - тип string, в данном аргументе необходимо указать, по какому адресу сервер авторизации должен передать код авторизации.
- DoneAuthUrl - тип string, в данном аргументе необходимо указать, какую страницу показать пользователю по окончании авторизации.
- OnlyHttps - данный аргумент указывает, нужно ли передавать куки только по защищённому протоколу или нет.
Для добавления конкретных сервисов авторизации необходимо вызывать функцию AddService()
.
В качестве аргумента в неё передаётся объект соответствующий интерфейсу OAuthorizator.
Точкой старта процедуры инициализации является один из адресов (страница входа),
который присваивается кнопке или ссылке. Эти адреса, принадлежащие каждому определённому сервису
передаются в один из JS-скриптов в виде констант. Текст объявления констант получаем с помощью
третьей функции GetSettingsJS()
. Текст объявления адреса входа каждого сервиса имеет вид:
const OAUTH_%1_URL = "%2";
, где
- %1 - имя сервиса капсом (YANDEX, GOOGLE и т. п.)
- %2 - сам адрес (URL)
Полный пример использования выглядит так:
package main
// Раздел импорта
import (
"fmt"
"net/http"
"bitbucket.org/aaklenov/oauth"
"bitbucket.org/aaklenov/oauth_google"
)
var g_oauth oauth.OAuthCollect
func main() {
err := g_oauth.Init("http://example.com:9000/oauth_verification_code", "http://example.com:9000", true)
oauth_go := OAuthGoogle { // oauth_google.
ClientId: "xxx.apps.googleusercontent.com",
ClientSecret: "yyy",
}
g_oauth.AddService(&oauth_go)
http.HandleFunc("/", HomeRouterHandler)
err = http.ListenAndServe(":9000", nil)
if err != nil {
fmt.Println("ListenAndServe: ", err)
}
}
func HomeRouterHandler(w http.ResponseWriter, r *http.Request) {
if (r.URL.Path == "/js/settings.js") {
oauth_settings := g_oauth.GetSettingsJS()
fmt.Fprintf(w, oauth_settings)
}
}
Объект интерфейса должен содержать три функции:
type OAuthorizator interface {
ServiceName() (string)
LoginURL(verification_code_callback_url string, state string) (string)
OnRecieveVerificationCode(code string, u *UserData) (error)
}
Функция ServiceName()
должна возвращать имя сервиса, например: google.
Функция LoginURL()
должна возвращать адрес страницы, на которой пользователя спросят,
разрешает он или нет такому-то веб-приложению доступ к имени и электронной почте.
В ней передаётся адрес веб-приложения, куда нужно передать код авторизации. А также
передаётся state
- строка, которую нужно записать в одноимённый get-параметр адреса.
В этой строке содержится имя сервиса и специальный токен, который помогает определить,
что вызов по адресу verification_code_callback_url
исходит именно от OAuth-сервиса,
и соответственно параметры, передаваемые в нём, не нанесут вред (впрочем, значения
параметров нигде не сохраняются и обычно передаются обратно в сервис "как есть").
Функция OnRecieveVerificationCode()
вызывается, когда получен код авторизации и
он проверен, что это именно он, а не посторонний вызов. При получении кода авторизации
экземпля интерфейса должен запросить токен. А далее по токену - получить данные пользователя
и передать их в структуру oauth.UserData
. К заполнению требуется следующие поля:
- ExtId, тип string - содержит идентификатор клиента в сервисе
- Name, тип string - содержит имя пользователя
- Email, тип string - содержит адрес электронной почты пользователя
Содержимое структуры:
type UserData struct {
UserId int64
Name string
Email string
ExtId string
OAuthServiceName string
}