initial commit

This commit is contained in:
Konstantin Lepa
2019-02-18 16:57:54 +03:00
committed by Nikolay Stupak
commit 6658817311
25 changed files with 2742 additions and 0 deletions

View File

@ -0,0 +1,53 @@
/*
Copyright (C) JSC iCore - All Rights Reserved
Unauthorized copying of this file, via any medium is strictly prohibited
Proprietary and confidential
Written by Konstantin Lepa <klepa@i-core.ru>, July 2018
*/
package hydra
import (
"github.com/pkg/errors"
"gopkg.i-core.ru/werther/internal/oauth2"
)
// ConsentReqDoer fetches information on the OAuth2 request and then accept or reject the requested authentication process.
type ConsentReqDoer struct {
hydraURL string
}
// NewConsentRequest creates a ConsentRequest.
func NewConsentReqDoer(hydraURL string) *ConsentReqDoer {
return &ConsentReqDoer{hydraURL: hydraURL}
}
// InitiateRequest fetches information on the OAuth2 request.
func (crd *ConsentReqDoer) InitiateRequest(challenge string) (*oauth2.ReqInfo, error) {
ri, err := initiateRequest(consent, crd.hydraURL, challenge)
return ri, errors.Wrap(err, "failed to initiate consent request")
}
// Accept accepts the requested authentication process, and returns redirect URI.
func (crd *ConsentReqDoer) AcceptConsentRequest(challenge string, remember bool, rememberFor int, grantScope []string, idToken interface{}) (string, error) {
type session struct {
IDToken interface{} `json:"id_token,omitempty"`
}
data := struct {
GrantScope []string `json:"grant_scope"`
Remember bool `json:"remember"`
RememberFor int `json:"remember_for"`
Session session `json:"session,omitempty"`
}{
GrantScope: grantScope,
Remember: remember,
RememberFor: rememberFor,
Session: session{
IDToken: idToken,
},
}
redirectURI, err := acceptRequest(consent, crd.hydraURL, challenge, data)
return redirectURI, errors.Wrap(err, "failed to accept consent request")
}

View File

@ -0,0 +1,136 @@
/*
Copyright (C) JSC iCore - All Rights Reserved
Unauthorized copying of this file, via any medium is strictly prohibited
Proprietary and confidential
Written by Konstantin Lepa <klepa@i-core.ru>, July 2018
*/
package hydra
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"gopkg.i-core.ru/werther/internal/oauth2"
)
type reqType string
const (
login reqType = "login"
consent reqType = "consent"
)
func initiateRequest(typ reqType, hydraURL, challenge string) (*oauth2.ReqInfo, error) {
ref, err := url.Parse(fmt.Sprintf("oauth2/auth/requests/%s/%s", string(typ), challenge))
if err != nil {
return nil, err
}
u, err := parseURL(hydraURL)
if err != nil {
return nil, err
}
u = u.ResolveReference(ref)
resp, err := http.Get(u.String())
if err != nil {
return nil, err
}
if err = checkResponse(resp); err != nil {
return nil, err
}
data, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
var ri oauth2.ReqInfo
if err := json.Unmarshal(data, &ri); err != nil {
return nil, err
}
return &ri, nil
}
func checkResponse(resp *http.Response) error {
if resp.StatusCode >= 200 && resp.StatusCode <= 302 {
return nil
}
if resp.StatusCode == 404 {
return oauth2.ErrChallengeNotFound
}
data, err := ioutil.ReadAll(resp.Body)
if err != nil {
return err
}
type errorResult struct {
Message string `json:"error"`
}
var rs errorResult
if err := json.Unmarshal(data, &rs); err != nil {
return err
}
switch resp.StatusCode {
case 401:
return oauth2.ErrUnauthenticated
case 409:
return oauth2.ErrChallengeExpired
default:
return fmt.Errorf("bad HTTP status code %d", resp.StatusCode)
}
}
func acceptRequest(typ reqType, hydraURL, challenge string, data interface{}) (string, error) {
ref, err := url.Parse(fmt.Sprintf("oauth2/auth/requests/%s/%s/accept", string(typ), challenge))
if err != nil {
return "", err
}
u, err := parseURL(hydraURL)
if err != nil {
return "", err
}
u = u.ResolveReference(ref)
body, err := json.Marshal(data)
if err != nil {
return "", err
}
r, err := http.NewRequest(http.MethodPut, u.String(), bytes.NewBuffer(body))
if err != nil {
return "", err
}
r.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(r)
if err != nil {
return "", err
}
defer resp.Body.Close()
if err := checkResponse(resp); err != nil {
return "", err
}
type result struct {
RedirectTo string `json:"redirect_to"`
}
var rs result
dec := json.NewDecoder(resp.Body)
if err := dec.Decode(&rs); err != nil {
return "", err
}
return rs.RedirectTo, nil
}
func parseURL(s string) (*url.URL, error) {
if len(s) > 0 && s[len(s)-1] != '/' {
s += "/"
}
u, err := url.Parse(s)
if err != nil {
return nil, err
}
return u, nil
}

View File

@ -0,0 +1,46 @@
/*
Copyright (C) JSC iCore - All Rights Reserved
Unauthorized copying of this file, via any medium is strictly prohibited
Proprietary and confidential
Written by Konstantin Lepa <klepa@i-core.ru>, July 2018
*/
package hydra
import (
"github.com/pkg/errors"
"gopkg.i-core.ru/werther/internal/oauth2"
)
// LoginReqDoer fetches information on the OAuth2 request and then accept or reject the requested authentication process.
type LoginReqDoer struct {
hydraURL string
}
// NewLoginRequest creates a LoginRequest.
func NewLoginReqDoer(hydraURL string) *LoginReqDoer {
return &LoginReqDoer{hydraURL: hydraURL}
}
// InitiateRequest fetches information on the OAuth2 request.
func (lrd *LoginReqDoer) InitiateRequest(challenge string) (*oauth2.ReqInfo, error) {
ri, err := initiateRequest(login, lrd.hydraURL, challenge)
return ri, errors.Wrap(err, "failed to initiate login request")
}
// Accept accepts the requested authentication process, and returns redirect URI.
func (lrd *LoginReqDoer) AcceptLoginRequest(challenge string, remember bool, rememberFor int, subject string) (string, error) {
data := struct {
Remember bool `json:"remember"`
RememberFor int `json:"remember_for"`
Subject string `json:"subject"`
}{
Remember: remember,
RememberFor: rememberFor,
Subject: subject,
}
redirectURI, err := acceptRequest(login, lrd.hydraURL, challenge, data)
return redirectURI, errors.Wrap(err, "failed to accept login request")
}

29
internal/oauth2/oauth2.go Normal file
View File

@ -0,0 +1,29 @@
/*
Copyright (C) JSC iCore - All Rights Reserved
Unauthorized copying of this file, via any medium is strictly prohibited
Proprietary and confidential
Written by Konstantin Lepa <klepa@i-core.ru>, February 2019
*/
package oauth2
import "errors"
var (
// ErrUnauthenticated is an error that happens when authentication is failed.
ErrUnauthenticated = errors.New("unauthenticated")
// ErrChallengeNotFound is an error that happens when an unknown challenge is used.
ErrChallengeNotFound = errors.New("challenge not found")
// ErrChallengeExpired is an error that happens when a challenge is already used.
ErrChallengeExpired = errors.New("challenge expired")
)
// ReqInfo contains information on an ongoing login or consent request.
type ReqInfo struct {
Challenge string `json:"challenge"`
RequestedScopes []string `json:"requested_scope"`
Skip bool `json:"skip"`
Subject string `json:"subject"`
}