// アクセストークンを使って、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 }
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 }
// 認可コードを使って、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 }
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) } }