コード例 #1
0
ファイル: values_test.go プロジェクト: realglobe-Inc/edo-auth
func newTestRequestWithParams(hndl *handler, idp, subIdp idpdb.Element, params, subParams map[string]interface{}) (*http.Request, error) {
	r, err := http.NewRequest("GET", "http://localhost/coop", nil)
	if err != nil {
		return nil, erro.Wrap(err)
	}

	codTok := jwt.New()
	codTok.SetHeader("alg", test_idpSigAlg)
	codTok.SetClaim("iss", idp.Id())
	codTok.SetClaim("sub", test_cod)
	codTok.SetClaim("aud", audience.New(hndl.selfId))
	codTok.SetClaim("from_client", test_frTa.Id())
	codTok.SetClaim("user_tag", test_acntTag)
	codTok.SetClaim("user_tags", []string{test_subAcnt1Tag})
	codTok.SetClaim("ref_hash", test_refHash)
	for k, v := range params {
		codTok.SetClaim(k, v)
	}
	if err := codTok.Sign(idp.Keys()); err != nil {
		return nil, erro.Wrap(err)
	}
	data, err := codTok.Encode()
	if err != nil {
		return nil, erro.Wrap(err)
	}
	r.Header.Set("X-Edo-Code-Tokens", string(data))

	subCodTok := jwt.New()
	subCodTok.SetHeader("alg", test_subIdpSigAlg)
	subCodTok.SetClaim("iss", subIdp.Id())
	subCodTok.SetClaim("sub", test_subCod)
	subCodTok.SetClaim("aud", audience.New(hndl.selfId))
	subCodTok.SetClaim("user_tags", []string{test_subAcnt2Tag})
	subCodTok.SetClaim("ref_hash", test_refHash)
	for k, v := range subParams {
		subCodTok.SetClaim(k, v)
	}
	if err := subCodTok.Sign(subIdp.Keys()); err != nil {
		return nil, erro.Wrap(err)
	}
	subData, err := subCodTok.Encode()
	if err != nil {
		return nil, erro.Wrap(err)
	}
	r.Header.Add("X-Edo-Code-Tokens", string(subData))

	return r, nil
}
コード例 #2
0
ファイル: values_test.go プロジェクト: realglobe-Inc/edo-auth
func newTestMainIdpResponseWithParams(hndl *handler, idp idpdb.Element, params, idsTokParams map[string]interface{}) (status int, header http.Header, body []byte, err error) {
	now := time.Now()

	idsTok := jwt.New()
	idsTok.SetHeader("alg", test_idpSigAlg)
	idsTok.SetClaim("iss", idp.Id())
	idsTok.SetClaim("sub", test_frTa.Id())
	idsTok.SetClaim("aud", audience.New(hndl.selfId))
	idsTok.SetClaim("exp", now.Add(time.Minute).Unix())
	idsTok.SetClaim("iat", now.Unix())
	idsTok.SetClaim("ids", map[string]map[string]interface{}{
		test_acntTag: {
			"sub":   test_acntId,
			"email": test_acntEmail,
		},
		test_subAcnt1Tag: {
			"sub":   test_subAcnt1Id,
			"email": test_subAcnt1Email,
		},
	})
	for k, v := range idsTokParams {
		idsTok.SetClaim(k, v)
	}
	if err := idsTok.Sign(idp.Keys()); err != nil {
		return 0, nil, nil, erro.Wrap(err)
	}
	data, err := idsTok.Encode()
	if err != nil {
		return 0, nil, nil, erro.Wrap(err)
	}

	m := map[string]interface{}{
		"access_token": test_tok,
		"token_type":   "Bearer",
		"expires_in":   1234,
		"scope":        "openid email",
		"ids_token":    string(data),
	}
	for k, v := range params {
		if v == nil {
			delete(m, k)
		} else {
			m[k] = v
		}
	}
	body, err = json.Marshal(m)
	if err != nil {
		return 0, nil, nil, erro.Wrap(err)
	}

	return http.StatusOK, http.Header{"Content-Type": {"application/json"}}, body, nil
}
コード例 #3
0
ファイル: values_test.go プロジェクト: realglobe-Inc/edo-auth
func newTestTokenResponse(page *Page, idp idpdb.Element, clms map[string]interface{}) (status int, hader http.Header, body []byte, err error) {
	m := map[string]interface{}{
		"access_token": test_tok,
		"token_type":   "Bearer",
		"expires_in":   3600,
		"scope":        "openid email",
	}
	idTok := jwt.New()
	idTok.SetHeader("alg", test_idpSigAlg)
	idTok.SetClaim("iss", idp.Id())
	idTok.SetClaim("sub", test_acntId)
	idTok.SetClaim("aud", page.selfId)
	now := time.Now()
	idTok.SetClaim("exp", now.Add(time.Minute).Unix())
	idTok.SetClaim("iat", now.Unix())
	idTok.SetClaim("nonce", test_nonc)
	hGen := jwt.HashGenerator(test_idpSigAlg)
	if !hGen.Available() {
		return 0, nil, nil, erro.New("unsupported algorithm " + test_idpSigAlg)
	}
	idTok.SetClaim("at_hash", hashutil.Hashing(hGen.New(), []byte(test_tok)))
	for k, v := range clms {
		idTok.SetClaim(k, v)
	}
	if err := idTok.Sign(idp.Keys()); err != nil {
		return 0, nil, nil, erro.Wrap(err)
	}
	data, err := idTok.Encode()
	if err != nil {
		return 0, nil, nil, erro.Wrap(err)
	}
	m["id_token"] = string(data)

	body, err = json.Marshal(m)
	if err != nil {
		return 0, nil, nil, erro.Wrap(err)
	}

	return http.StatusOK, nil, body, nil
}
コード例 #4
0
ファイル: values_test.go プロジェクト: realglobe-Inc/edo-auth
func newCallbackRequestWithIdToken(page *Page, idp idpdb.Element, clms map[string]interface{}) (*http.Request, error) {
	q := url.Values{}
	q.Set("code", test_cod)
	q.Set("state", test_stat)
	idTok := jwt.New()
	idTok.SetHeader("alg", test_idpSigAlg)
	idTok.SetClaim("iss", idp.Id())
	idTok.SetClaim("sub", test_acntId)
	idTok.SetClaim("aud", page.selfId)
	now := time.Now()
	idTok.SetClaim("exp", now.Add(time.Minute).Unix())
	idTok.SetClaim("iat", now.Unix())
	idTok.SetClaim("nonce", test_nonc)
	hGen := jwt.HashGenerator(test_idpSigAlg)
	if !hGen.Available() {
		return nil, erro.New("unsupported algorithm " + test_idpSigAlg)
	}
	idTok.SetClaim("c_hash", hashutil.Hashing(hGen.New(), []byte(test_cod)))
	for k, v := range clms {
		idTok.SetClaim(k, v)
	}
	if err := idTok.Sign(idp.Keys()); err != nil {
		return nil, erro.Wrap(err)
	}
	data, err := idTok.Encode()
	if err != nil {
		return nil, erro.Wrap(err)
	}
	q.Set("id_token", string(data))
	r, err := http.NewRequest("GET", "http://localhost/callback?"+q.Encode(), nil)
	if err != nil {
		return nil, erro.Wrap(err)
	}
	r.AddCookie(&http.Cookie{
		Name:  page.sessLabel,
		Value: test_sessId,
	})
	return r, nil
}
コード例 #5
0
ファイル: handler.go プロジェクト: realglobe-Inc/edo-auth
// TA 認証用署名をつくる。
func makeAssertion(hndl *handler, keys []jwk.Key, aud string) ([]byte, error) {
	ass := jwt.New()
	ass.SetHeader(tagAlg, hndl.sigAlg)
	if hndl.sigKid != "" {
		ass.SetHeader(tagKid, hndl.sigKid)
	}
	ass.SetClaim(tagIss, hndl.selfId)
	ass.SetClaim(tagSub, hndl.selfId)
	ass.SetClaim(tagAud, aud)
	ass.SetClaim(tagJti, hndl.idGen.String(hndl.jtiLen))
	now := time.Now()
	ass.SetClaim(tagExp, now.Add(hndl.jtiExpIn).Unix())
	ass.SetClaim(tagIat, now.Unix())
	if err := ass.Sign(keys); err != nil {
		return nil, erro.Wrap(err)
	}
	data, err := ass.Encode()
	if err != nil {
		return nil, erro.Wrap(err)
	}

	return data, nil
}
コード例 #6
0
func (this *environment) callbackServe(w http.ResponseWriter, r *http.Request) error {
	if this.sessId == "" {
		return erro.Wrap(server.NewError(http.StatusBadRequest, "no session ", nil))
	}

	sess, err := this.sessDb.Get(this.sessId)
	if err != nil {
		return erro.Wrap(err)
	} else if sess == nil {
		return erro.Wrap(server.NewError(http.StatusBadRequest, "declared user session is not exist", nil))
	}
	this.sess = sess
	log.Debug(this.logPref, "Declared user session is exist")

	savedDate := sess.Date()
	sess.Invalidate()
	if ok, err := this.sessDb.Replace(sess, savedDate); err != nil {
		return erro.Wrap(err)
	} else if !ok {
		return erro.Wrap(server.NewError(http.StatusBadRequest, "reused user session", nil))
	}

	req, err := parseCallbackRequest(r)
	if err != nil {
		return erro.Wrap(server.NewError(http.StatusBadRequest, erro.Unwrap(err).Error(), err))
	}

	log.Debug(this.logPref, "Parsed callback request")

	if req.state() != sess.State() {
		return erro.Wrap(server.NewError(http.StatusForbidden, "invalid state", nil))
	}

	var idp idpdb.Element
	var attrs1 map[string]interface{}

	if sess.IdProvider() != "" {
		idp, err = this.idpDb.Get(sess.IdProvider())
		if err != nil {
			return erro.Wrap(err)
		} else if idp == nil {
			return erro.Wrap(server.NewError(http.StatusBadRequest, "ID provider "+sess.IdProvider()+" is not exist", nil))
		}
		log.Debug(this.logPref, "ID provider "+idp.Id()+" is exist")
	} else {
		idTok, err := parseIdToken(req.idToken())
		if err != nil {
			return erro.Wrap(server.NewError(http.StatusBadRequest, erro.Unwrap(err).Error(), err))
		}

		idp, err = this.idpDb.Get(idTok.idProvider())
		if err != nil {
			return erro.Wrap(err)
		} else if idp == nil {
			return erro.Wrap(server.NewError(http.StatusBadRequest, "ID provider "+idTok.idProvider()+" is not exist", nil))
		}
		log.Debug(this.logPref, "ID provider "+idp.Id()+" is exist")

		if idTok.nonce() != sess.Nonce() {
			return erro.Wrap(server.NewError(http.StatusForbidden, "invalid nonce", nil))
		} else if err := idTok.verify(idp.Keys()); err != nil {
			return erro.Wrap(server.NewError(http.StatusForbidden, erro.Unwrap(err).Error(), err))
		} else if err := idTok.verifyCodeHash(req.code()); err != nil {
			return erro.Wrap(server.NewError(http.StatusForbidden, erro.Unwrap(err).Error(), err))
		}
		attrs1 = idTok.attributes()
		log.Debug(this.logPref, "ID token is OK")
	}

	// アクセストークンを取得する。
	tok, idTok, err := this.getAccessToken(req, idp)
	if err != nil {
		return erro.Wrap(err)
	}

	// アカウント情報を取得する。
	attrs2, err := this.getAccountInfo(req, tok, idp)
	if err != nil {
		return erro.Wrap(err)
	}

	// アカウント情報をまとめる。
	jt := jwt.New()
	jt.SetHeader(tagAlg, tagNone)
	for _, m := range []map[string]interface{}{attrs1, idTok.attributes(), attrs2} {
		for k, v := range m {
			jt.SetClaim(k, v)
		}
	}
	jt.SetClaim(tagAt_tag, tok.Tag())
	jt.SetClaim(tagAt_exp, tok.Expires().Unix())
	buff, err := jt.Encode()
	if err != nil {
		return erro.Wrap(err)
	}

	// フロントエンドのためにセッション期限を延長する。
	now := time.Now()
	http.SetCookie(w, this.newCookie(sess.Id(), now.Add(-time.Second)))
	http.SetCookie(w, this.newFrontCookie(this.idGen.String(this.fsessLen), now.Add(this.fsessExpIn)))
	log.Info(this.logPref, "Upgrade user session to frontend session")

	// フロントエンドが使うので保存しなくて良い。

	w.Header().Add(tagX_auth_user, string(buff))
	w.Header().Add(tagCache_control, tagNo_store)
	w.Header().Add(tagPragma, tagNo_cache)

	http.Redirect(w, r, sess.Path(), http.StatusFound)
	log.Info(this.logPref, "Redirect to "+sess.Path())
	return nil
}
コード例 #7
0
ファイル: page.go プロジェクト: realglobe-Inc/edo-auth
// 認可コードを使って、ID プロバイダからアクセストークンを取得する。
func (this *environment) getAccessToken(req *callbackRequest, idp idpdb.Element) (*token.Element, *idToken, error) {
	keys, err := this.keyDb.Get()
	if err != nil {
		return nil, nil, erro.Wrap(err)
	}

	queries := url.Values{}
	// grant_type
	queries.Set(tagGrant_type, tagAuthorization_code)
	// code
	queries.Set(tagCode, req.code())
	// redirect_uri
	queries.Set(tagRedirect_uri, this.sess.RedirectUri())
	// client_id
	queries.Set(tagClient_id, this.sess.Ta())
	// client_assertion_type
	queries.Set(tagClient_assertion_type, cliAssTypeJwt_bearer)
	// client_assertion
	ass := jwt.New()
	now := time.Now()
	ass.SetHeader(tagAlg, this.sigAlg)
	ass.SetClaim(tagIss, this.sess.Ta())
	ass.SetClaim(tagSub, this.sess.Ta())
	ass.SetClaim(tagAud, idp.TokenUri())
	ass.SetClaim(tagJti, this.idGen.String(this.jtiLen))
	ass.SetClaim(tagExp, now.Add(this.jtiExpIn).Unix())
	ass.SetClaim(tagIat, now.Unix())
	if err := ass.Sign(keys); err != nil {
		return nil, nil, erro.Wrap(err)
	}
	buff, err := ass.Encode()
	if err != nil {
		return nil, nil, erro.Wrap(err)
	}
	queries.Set(tagClient_assertion, string(buff))

	tokReq, err := http.NewRequest("POST", idp.TokenUri(), strings.NewReader(queries.Encode()))
	if err != nil {
		return nil, nil, erro.Wrap(err)
	}
	tokReq.Header.Set(tagContent_type, contTypeForm)

	server.LogRequest(level.DEBUG, tokReq, this.debug, this.logPref)
	resp, err := http.DefaultClient.Do(tokReq)
	if err != nil {
		return nil, nil, erro.Wrap(err)
	}
	defer resp.Body.Close()
	server.LogResponse(level.DEBUG, resp, this.debug, this.logPref)
	log.Info(this.logPref, "Got token response from "+idp.Id())

	tokResp, err := parseTokenResponse(resp)
	if err != nil {
		return nil, nil, erro.Wrap(server.NewError(http.StatusForbidden, "cannot get access token", nil))
	}
	tok := token.New(tokResp.token(), this.idGen.String(this.tokTagLen), tokResp.expires(), idp.Id(), tokResp.scope())
	log.Info(this.logPref, "Got access token "+logutil.Mosaic(tok.Id()))

	if err := this.tokDb.Save(tok, time.Now().Add(this.tokDbExpIn)); err != nil {
		return nil, nil, erro.Wrap(err)
	}
	log.Info(this.logPref, "Saved access token "+logutil.Mosaic(tok.Id()))

	idTok, err := parseIdToken(tokResp.idToken())
	if err != nil {
		return nil, nil, erro.Wrap(server.NewError(http.StatusForbidden, erro.Unwrap(err).Error(), err))
	} else if idTok.nonce() != this.sess.Nonce() {
		return nil, nil, erro.Wrap(server.NewError(http.StatusForbidden, "invalid nonce", nil))
	} else if err := idTok.verify(idp.Keys()); err != nil {
		return nil, nil, erro.Wrap(server.NewError(http.StatusForbidden, erro.Unwrap(err).Error(), err))
	} else if idTok.tokenHash() != nil {
		if err := idTok.verifyTokenHash(tok.Id()); err != nil {
			return nil, nil, erro.Wrap(server.NewError(http.StatusForbidden, erro.Unwrap(err).Error(), err))
		}
	}
	log.Info(this.logPref, "ID token is OK")

	return tok, idTok, nil
}
コード例 #8
0
ファイル: handler.go プロジェクト: realglobe-Inc/edo-auth
func (this *environment) serve(w http.ResponseWriter, r *http.Request) error {
	req, err := parseRequest(r)
	if err != nil {
		return erro.Wrap(idperr.New(idperr.Invalid_request, erro.Unwrap(err).Error(), http.StatusBadRequest, err))
	}

	var acntTag string
	tags := map[string]bool{}
	var refHash string
	type idpUnit struct {
		idp    idpdb.Element
		codTok *codeToken
	}
	units := []*idpUnit{}
	for _, rawCodTok := range req.codeTokens() {
		codTok, err := parseCodeToken(rawCodTok)
		if err != nil {
			return erro.Wrap(idperr.New(idperr.Invalid_request, erro.Unwrap(err).Error(), http.StatusBadRequest, err))
		} else if !codTok.audience()[this.selfId] {
			return erro.Wrap(idperr.New(idperr.Invalid_request, "invalid audience", http.StatusBadRequest, nil))
		} else if codTok.referralHash() == "" && len(req.codeTokens()) > 1 {
			return erro.Wrap(idperr.New(idperr.Invalid_request, "no referral hash", http.StatusBadRequest, nil))
		} else if codTok.accountTag() != "" {
			if acntTag != "" {
				return erro.Wrap(idperr.New(idperr.Invalid_request, "two main token", http.StatusBadRequest, nil))
			}
			acntTag = codTok.accountTag()
			log.Debug(this.logPref, "Main account tag is "+acntTag)
			if codTok.fromTa() == "" {
				return erro.Wrap(idperr.New(idperr.Invalid_request, "no from-TA in main token", http.StatusBadRequest, nil))
			}
			log.Debug(this.logPref, "From-TA is "+codTok.fromTa())
		} else if len(codTok.accountTags()) == 0 {
			return erro.Wrap(idperr.New(idperr.Invalid_request, "no account tags in sub token", http.StatusBadRequest, nil))
		}

		for tag := range codTok.accountTags() {
			if tags[tag] {
				return erro.Wrap(idperr.New(idperr.Invalid_request, "tag "+tag+" overlaps", http.StatusBadRequest, nil))
			}
			tags[tag] = true
			log.Debug(this.logPref, "Account tag is "+tag)
		}

		if codTok.referralHash() != "" {
			if refHash == "" {
				refHash = codTok.referralHash()
			} else if codTok.referralHash() != refHash {
				return erro.Wrap(idperr.New(idperr.Invalid_request, "invalid referral hash", http.StatusBadRequest, nil))
			}
		}

		var idp idpdb.Element
		if idp, err = this.idpDb.Get(codTok.idProvider()); err != nil {
			return erro.Wrap(err)
		} else if idp == nil {
			return erro.Wrap(idperr.New(idperr.Invalid_request, "ID provider "+codTok.idProvider()+" is not exist", http.StatusBadRequest, nil))
		}

		log.Debug(this.logPref, "ID provider "+idp.Id()+" is exist")

		if err := codTok.verify(idp.Keys()); err != nil {
			return erro.Wrap(idperr.New(idperr.Invalid_request, erro.Unwrap(err).Error(), http.StatusBadRequest, err))
		}

		log.Debug(this.logPref, "Verified cooperation code")

		units = append(units, &idpUnit{idp, codTok})
	}
	if acntTag == "" {
		return erro.Wrap(idperr.New(idperr.Invalid_request, "no main account tag", http.StatusBadRequest, nil))
	}

	log.Debug(this.logPref, "Cooperation codes are OK")

	var tok *token.Element
	var mainAttrs map[string]interface{}
	tagToAttrs := map[string]map[string]interface{}{}
	var frTa string
	for _, unit := range units {
		var tToA map[string]map[string]interface{}
		var fT string
		if unit.codTok.accountTag() != "" {
			fT, tok, tToA, err = this.getInfoFromMainIdProvider(unit.idp, unit.codTok)
			if err != nil {
				return erro.Wrap(err)
			}
			log.Debug(this.logPref, "Got account info from main ID provider "+unit.idp.Id())
		} else {
			fT, tToA, err = this.getInfoFromSubIdProvider(unit.idp, unit.codTok)
			if err != nil {
				return erro.Wrap(err)
			}
			log.Debug(this.logPref, "Got account info from sub ID provider "+unit.idp.Id())
		}
		for tag, attrs := range tToA {
			if tag == acntTag {
				attrs[tagIss] = unit.idp.Id()
				attrs[tagAt_tag] = tok.Tag()
				attrs[tagAt_exp] = tok.Expires().Unix()
				mainAttrs = attrs
			} else {
				attrs[tagIss] = unit.idp.Id()
				tagToAttrs[tag] = attrs
			}
		}
		if frTa == "" {
			frTa = fT
		} else if frTa != fT {
			return erro.Wrap(idperr.New(idperr.Invalid_request, "two from-TA ID", http.StatusBadRequest, nil))
		}
	}

	log.Debug(this.logPref, "Got all account info")

	jt := jwt.New()
	jt.SetHeader(tagAlg, tagNone)
	for k, v := range mainAttrs {
		jt.SetClaim(k, v)
	}
	mainInfo, err := jt.Encode()
	if err != nil {
		return erro.Wrap(err)
	}

	var relInfo []byte
	sessFlag := true
	if len(tagToAttrs) > 0 {
		jt = jwt.New()
		jt.SetHeader(tagAlg, tagNone)
		for acntTag, attrs := range tagToAttrs {
			jt.SetClaim(acntTag, attrs)
			if sessFlag {
				for k := range attrs {
					if !sessionEnable(k) {
						sessFlag = false
					}
				}
			}
		}
		relInfo, err = jt.Encode()
		if err != nil {
			return erro.Wrap(err)
		}
	}

	w.Header().Set(tagX_auth_user, string(mainInfo))
	w.Header().Set(tagX_auth_user_tag, acntTag)
	w.Header().Set(tagX_auth_from_id, frTa)
	if relInfo != nil {
		w.Header().Set(tagX_auth_users, string(relInfo))
	}
	if sessFlag {
		sessId := this.idGen.String(this.sessLen)
		http.SetCookie(w, this.handler.newCookie(sessId, tok.Expires()))
		log.Debug(this.logPref, "Report session "+logutil.Mosaic(sessId))
	}
	return nil
}