func TestCoopResponse(t *testing.T) {
	tok := "McnSl40-QRtAxBoBOmj9GJfALyNdJy"
	tokType := "Bearer"
	expIn := 2817 * time.Second
	scop := strsetutil.New("openid")
	idsTok := []byte("eyJhbGciOiJSUzI1NiJ9.eyJhdWQiOiJodHRwOi8vbG9jYWxob3N0OjcwMDAiLCJleHAiOjE0MzI4MjI1MzMsImlhdCI6MTQzMjgwMDkzMywiaWRzIjp7Im1haW4iOnsic3ViIjoiWi0wWnRGZWlyaU1kWkZIYlNDR3ZDbHplclBYWkU5SGJKNWlaREx2X3hVWSJ9fSwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDoxNjA0Iiwic3ViIjoiaHR0cDovL2xvY2FsaG9zdDo3MDAwIn0.JRCRQRJkE5UTwniZqYf6Y-DncD58Qs0GQe5SF7A_K66nymZR9mUrXWj0rAG6NcXEsH5IpoFfcYUVLylx_XxyrKNK8ynGgiGwOblNW41rb2FmLmtFdf3Y03z9ID5hWJsEJmyup071s5WUeURAa3xv-h-t-cSBqXIv_R-TPWGyWSKVSe6lKudfOh74rrG_5IHheFs3hKiyw9viVJRV9sOekcrV93ppdUweguxx_AgSjFhdEh6atmUy5ft3Oz3doqfSQHQ2xMR_V9cFryzXrfJDyual4KBFMQHJ3LU2kyuoebKpHjwT5Iv3Bn-QY7U_SIO4vvcBuOcvjNyWyymla6KzPA")
	body, err := json.Marshal(map[string]interface{}{
		"access_token": tok,
		"token_type":   tokType,
		"expires_in":   int(expIn / time.Second),
		"scope":        requtil.ValueSetForm(scop),
		"ids_token":    string(idsTok),
	})
	if err != nil {
		t.Fatal(err)
	}

	r, err := http.ReadResponse(bufio.NewReader(io.MultiReader(
		strings.NewReader("HTTP/1.1 200 OK\r\n"+"Content-Type: application/json\r\n"+"\r\n"),
		bytes.NewReader(body),
	)), nil)
	if err != nil {
		t.Fatal(err)
	}

	resp, err := parseCoopResponse(r)
	if err != nil {
		t.Fatal(err)
	} else if resp.token() != tok {
		t.Error(resp.token())
		t.Fatal(tok)
	} else if resp.tokenType() != tokType {
		t.Error(resp.tokenType())
		t.Fatal(tokType)
	} else if resp.expiresIn() != expIn {
		t.Error(resp.expiresIn())
		t.Fatal(expIn)
	} else if !reflect.DeepEqual(resp.scope(), scop) {
		t.Error(resp.scope())
		t.Fatal(scop)
	} else if !bytes.Equal(resp.idsToken(), idsTok) {
		t.Error(resp.idsToken())
		t.Fatal(idsTok)
	}
}
func (this *environment) authServe(w http.ResponseWriter, r *http.Request) error {
	req, err := parseAuthRequest(r)
	if err != nil {
		return erro.Wrap(server.NewError(http.StatusBadRequest, erro.Unwrap(err).Error(), err))
	}

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

	authUri := req.authUri()
	queries := authUri.Query()

	// response_type
	var idp string
	respType := map[string]bool{tagCode: true}
	rawAuthUri := authUri.Scheme + "://" + authUri.Host + authUri.Path
	if idps, err := this.idpDb.Search(map[string]string{
		tagAuthorization_endpoint: "^" + rawAuthUri + "$",
	}); err != nil {
		return erro.Wrap(err)
	} else if len(idps) == 1 {
		idp = idps[0].Id()
		log.Debug(this.logPref, "Destination is in ID provider "+idp)
	} else {
		// ID プロバイダ選択サービスか何か。
		respType[tagId_token] = true
		log.Debug(this.logPref, "Destination "+rawAuthUri+" is not ID provider")
	}
	queries.Set(tagResponse_type, request.ValueSetForm(respType))

	// scope
	if scop := request.FormValueSet(queries.Get(tagScope)); !scop[tagOpenid] {
		scop[tagOpenid] = true
		queries.Set(tagScope, request.ValueSetForm(scop))
		log.Debug(this.logPref, `Added scope "`+tagOpenid+`"`)
	}

	// client_id
	ta := queries.Get(tagClient_id)
	if ta == "" {
		ta = this.selfId
		queries.Set(tagClient_id, ta)
		log.Debug(this.logPref, "Act as default TA "+ta)
	} else {
		log.Debug(this.logPref, "Act as TA "+ta)
	}

	// redirect_uri
	rediUri := queries.Get(tagRedirect_uri)
	if rediUri == "" {
		rediUri = this.rediUri
		queries.Set(tagRedirect_uri, rediUri)
		log.Debug(this.logPref, "Use default redirect uri "+rediUri)
	} else {
		log.Debug(this.logPref, "Use redirect uri "+rediUri)
	}

	// state
	stat := this.idGen.String(this.statLen)
	queries.Set(tagState, stat)
	log.Debug(this.logPref, "Use state "+logutil.Mosaic(stat))

	// nonce
	nonc := this.idGen.String(this.noncLen)
	queries.Set(tagNonce, nonc)
	log.Debug(this.logPref, "Use nonce "+logutil.Mosaic(nonc))

	authUri.RawQuery = queries.Encode()

	sess := asession.New(
		this.idGen.String(this.sessLen),
		time.Now().Add(this.sessExpIn),
		req.path(),
		idp,
		ta,
		rediUri,
		stat,
		nonc,
	)
	if err := this.sessDb.Save(sess, sess.Expires().Add(this.sessDbExpIn-this.sessExpIn)); err != nil {
		return erro.Wrap(err)
	}
	log.Info(this.logPref, "Generated user session "+logutil.Mosaic(sess.Id()))

	http.SetCookie(w, this.newCookie(sess.Id(), sess.Expires()))
	w.Header().Add(tagCache_control, tagNo_store)
	w.Header().Add(tagPragma, tagNo_cache)

	uri := authUri.String()
	http.Redirect(w, r, uri, http.StatusFound)
	log.Info(this.logPref, "Redirect to "+uri)
	return nil
}