示例#1
0
// アクセストークンを使って、ID プロバイダからアカウント情報を取得する。
func (this *environment) getAccountInfo(req *callbackRequest, tok *token.Element, idp idpdb.Element) (attrs map[string]interface{}, err error) {
	acntReq, err := http.NewRequest("GET", idp.AccountUri(), nil)
	if err != nil {
		return nil, erro.Wrap(err)
	}
	acntReq.Header.Set(tagAuthorization, tagBearer+" "+tok.Id())

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

	if err := json.NewDecoder(resp.Body).Decode(&attrs); err != nil {
		return nil, erro.Wrap(err)
	}
	log.Info(this.logPref, "Got account info")

	return attrs, nil
}
示例#2
0
func (this *environment) getInfo(isMain bool, idp idpdb.Element, codTok *codeToken) (frTa string, tok *token.Element, tagToAttrs map[string]map[string]interface{}, err error) {
	params := map[string]interface{}{}

	// grant_type
	params[tagGrant_type] = tagCooperation_code

	// code
	params[tagCode] = codTok.code()

	// claims
	if isMain {
		// TODO 受け取り方を考えないと。
	}

	// user_claims
	// TODO 受け取り方を考えないと。

	// client_assertion_type
	params[tagClient_assertion_type] = cliAssTypeJwt_bearer

	// client_assertion
	keys, err := this.keyDb.Get()
	if err != nil {
		return "", nil, nil, erro.Wrap(err)
	}
	ass, err := makeAssertion(this.handler, keys, idp.CoopToUri())
	if err != nil {
		return "", nil, nil, erro.Wrap(err)
	}
	params[tagClient_assertion] = string(ass)

	data, err := json.Marshal(params)
	if err != nil {
		return "", nil, nil, erro.Wrap(err)
	}

	r, err := http.NewRequest("POST", idp.CoopToUri(), bytes.NewReader(data))
	r.Header.Set(tagContent_type, contTypeJson)
	log.Debug(this.logPref, "Made main cooperation-to request")

	server.LogRequest(level.DEBUG, r, this.debug, this.logPref)
	resp, err := this.conn.Do(r)
	if err != nil {
		return "", nil, nil, erro.Wrap(err)
	}
	defer resp.Body.Close()
	server.LogResponse(level.DEBUG, resp, this.debug, this.logPref)

	if resp.StatusCode != http.StatusOK {
		var buff struct {
			Error             string
			Error_description string
		}
		if err := json.NewDecoder(resp.Body).Decode(&buff); err != nil {
			return "", nil, nil, erro.Wrap(err)
		}
		return "", nil, nil, erro.Wrap(idperr.New(buff.Error, buff.Error_description, resp.StatusCode, nil))
	}
	coopResp, err := parseCoopResponse(resp)
	if err != nil {
		return "", nil, nil, erro.Wrap(idperr.New(idperr.Access_denied, erro.Unwrap(err).Error(), http.StatusForbidden, err))
	}

	idsTok, err := parseIdsToken(coopResp.idsToken())
	if err != nil {
		return "", nil, nil, erro.Wrap(idperr.New(idperr.Access_denied, erro.Unwrap(err).Error(), http.StatusForbidden, err))
	} else if err := idsTok.verify(idp.Keys()); err != nil {
		return "", nil, nil, erro.Wrap(idperr.New(idperr.Access_denied, erro.Unwrap(err).Error(), http.StatusForbidden, err))
	}

	tagToAttrs = map[string]map[string]interface{}{}
	for acntTag := range codTok.accountTags() {
		attrs := idsTok.attributes()[acntTag]
		if attrs == nil {
			return "", nil, nil, erro.Wrap(idperr.New(idperr.Access_denied, "cannot get sub account tagged by "+acntTag, http.StatusForbidden, nil))
		}
		tagToAttrs[acntTag] = attrs
	}

	if isMain {
		attrs := idsTok.attributes()[codTok.accountTag()]
		if attrs == nil {
			return "", nil, nil, erro.Wrap(idperr.New(idperr.Access_denied, "cannot get main account tagged by "+codTok.accountTag(), http.StatusForbidden, nil))
		}
		tagToAttrs[codTok.accountTag()] = attrs

		if coopResp.token() == "" {
			return "", nil, nil, erro.Wrap(idperr.New(idperr.Access_denied, "cannot get token", http.StatusForbidden, nil))
		}
		now := time.Now()
		tok = token.New(coopResp.token(), this.idGen.String(this.tokTagLen), now.Add(coopResp.expiresIn()), idsTok.idProvider(), coopResp.scope())
		log.Info(this.logPref, "Got access token "+logutil.Mosaic(tok.Id()))

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

	return idsTok.fromTa(), tok, tagToAttrs, nil
}
示例#3
0
// 認可コードを使って、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
}
示例#4
0
func testServer(t *testing.T, param *parameters, idpServ *testIdProvider) {
	idp := idpServ.info()

	errCh := make(chan error, 1)
	go func() {
		errCh <- serve(param)
	}()
	defer func() { param.shutCh <- struct{}{} }()

	selfUri, err := url.Parse("http://localhost:" + strconv.Itoa(param.socPort))
	if err != nil {
		t.Fatal(err)
	} else if err := waitServer(selfUri.String()+param.pathOk, errCh, time.Now().Add(time.Minute)); err != nil {
		t.Fatal(err)
	}

	cookJar, err := cookiejar.New(nil)
	if err != nil {
		t.Fatal(err)
	}
	noRedi := func(req *http.Request, via []*http.Request) error { return errors.New("redirect flag") }

	// 認証前リクエスト。
	reqPath := strings.TrimLeft(param.pathAuth, "/") + "/a/b/c"
	authReq, err := newAuthRequest(selfUri.String()+reqPath, idp.AuthUri())
	if err != nil {
		t.Fatal(err)
	}
	authResp, err := (&http.Client{Jar: cookJar, CheckRedirect: noRedi}).Do(authReq)
	if err != nil {
		if e, ok := err.(*url.Error); !ok || e.Err.Error() != "redirect flag" {
			t.Fatal(err)
		}
	}
	defer authResp.Body.Close()
	server.LogResponse(level.DEBUG, authResp, false)

	if authResp.StatusCode != http.StatusFound {
		t.Error(authResp.StatusCode)
		t.Fatal(http.StatusFound)
	}
	authUri, err := url.Parse(authResp.Header.Get("Location"))
	if err != nil {
		t.Fatal(err)
	} else if authUri := authUri.Scheme + "://" + authUri.Host + authUri.Path; authUri != idp.AuthUri() {
		t.Error(authUri)
		t.Fatal(idp.AuthUri())
	}
	sessExist := false
	for _, cook := range cookJar.Cookies(selfUri) {
		if cook.Name == param.asessLabel {
			sessExist = true
			break
		}
	}
	if !sessExist {
		t.Fatal("no new session")
	}
	authQ := authUri.Query()
	if len(authQ) == 0 {
		t.Fatal("no query")
	} else if respType := request.FormValueSet(authQ.Get("response_type")); len(respType) != 1 || !respType["code"] {
		t.Error(respType)
		t.Fatal("code")
	} else if scop := request.FormValueSet(authQ.Get("scope")); len(scop) != 1 || !scop["openid"] {
		t.Error(scop)
		t.Fatal("openid")
	} else if taId := authQ.Get("client_id"); taId != param.selfId {
		t.Error(taId)
		t.Fatal(param.selfId)
	} else if rediUri := authQ.Get("redirect_uri"); rediUri != param.rediUri {
		t.Error(rediUri)
		t.Fatal(param.rediUri)
	} else if authQ.Get("state") == "" {
		t.Fatal("no state")
	} else if authQ.Get("nonce") == "" {
		t.Fatal("no nonce")
	}

	// 認証後リクエスト。
	{
		s, h, b, err := newTestTokenResponse(param.selfId, authQ.Get("nonce"), idp)
		if err != nil {
			t.Fatal(err)
		}
		idpServ.addResponse(s, h, b)
	}
	{
		s, h, b, err := newTestAccountResponse(idp)
		if err != nil {
			t.Fatal(err)
		}
		idpServ.addResponse(s, h, b)
	}

	cbReq, err := newCallbackRequest(selfUri.String()+param.pathCb, authQ.Get("state"))
	if err != nil {
		t.Fatal(err)
	}
	cbResp, err := (&http.Client{Jar: cookJar, CheckRedirect: noRedi}).Do(cbReq)
	if err != nil {
		if e, ok := err.(*url.Error); !ok || e.Err.Error() != "redirect flag" {
			t.Fatal(err)
		}
	}
	defer cbResp.Body.Close()
	server.LogResponse(level.DEBUG, cbResp, false)

	if cbResp.StatusCode != http.StatusFound {
		t.Error(cbResp.StatusCode)
		t.Fatal(http.StatusFound)
	} else if uri, err := url.Parse(cbResp.Header.Get("Location")); err != nil {
		t.Fatal(err)
	} else if uri.Path != reqPath {
		t.Error(uri.Path)
		t.Fatal(reqPath)
	}
	var buff struct {
		Iss    string
		Sub    string
		At_tag string
		At_exp int
		Email  string
	}
	if jt, err := jwt.Parse([]byte(cbResp.Header.Get("X-Auth-User"))); err != nil {
		t.Fatal(err)
	} else if err := json.Unmarshal(jt.RawBody(), &buff); err != nil {
		t.Fatal(err)
	} else if buff.Iss != idp.Id() {
		t.Error(buff.Iss)
		t.Fatal(idp.Id())
	} else if buff.Sub != test_acntId {
		t.Error(buff.Sub)
		t.Fatal(test_acntId)
	} else if len(buff.At_tag) != param.tokTagLen {
		t.Error(len(buff.At_tag), buff.At_tag)
		t.Fatal(param.tokTagLen)
	} else if buff.At_exp == 0 {
		t.Fatal("no at_exp")
	} else if buff.Email != test_acntEmail {
		t.Error(buff.Email)
		t.Fatal(test_acntEmail)
	}
}