OAuth 2.0 Multiple Response Type Encoding Practicesの和訳風怪文書

以下は、OAuth 2.0 Multiple Response Type Encoding Practicesの和訳風怪文書です。 読んでいて意味がよくわからなかった部分については、🍙がついています。 具体的には、none の用途と、response_mode を混在すべき理由が理解できていません。

Abstract

本仕様はOAuth 2.0のスペース文字を含むResponse Typeの値を利用する認可リクエストに対するレスポンスの適切なエンコードのガイダンスを提供します。 さらに仕様では、いくつかの新しいレスポンスタイプの値を、OAuth Authorization Endpoint Response Types registryに登録します。

この仕様では、認可リクエストのパラメーター、'レスポンスモード'も定義します。これらの値は、認可エンドポイントから認可レスポンスのパラメーターを返却するためのメカニズムを認可サーバーに伝えるために利用されます。

1. Introduction

1.1. Requirements Notation and Conventions

1.2. Terminology

2. Response Types and Response Modes

レスポンスタイプリクエストパラメーター 'response_type' は、認可サーバーにエンドポイントからどのパラメーターが返却されるのかを含んだ、期待する認可処理フローを伝えます。 レスポンスモードリクエストパラメーター 'response_mode' は、認可エンドポイントから返却される認可レスポンスを返却するために使われるメカニズムを、認可サーバーに伝えます。 各レスポンスタイプの値は、レスポンスモードが指定されなかった場合に利用されるデフォルトのレスポンスモードも定義します。

2.1. Response Mode

本仕様は以下のOAuth認可リクエストパラメーターを定義します。

  • response_mode 任意の値。認可サーバーに、認可エンドポイントから認可レスポンスパラメーターを返却するのに利用されるメカニズムを伝えます。 レスポンスタイプが利用するデフォルトのレスポンスモードと同じ値を指定する場合、本パラメーターの利用は推奨されません。 本仕様は以下のレスポンスモードの値を定義します。

  • query このモードでは、認可レスポンスパラメーターはクライアントにリダイレクトする際の redirect_uri に付与されるクエリストリングにてエンコードされます。

  • fragment このモードでは、認可レスポンスパラメーターはクライアントにリダイレクトする際の redirect_uri に付与されるフラグメントにてエンコードされます。

OAuth 2.0 code レスポンスタイプのデフォルトのレスポンスモードは、クエリエンコーディングです。 OAuth 2.0 token レスポンスタイプのデフォルトのレスポンスモードは、フラグメントエンコーディングです。

追加のレスポンスモードを定義している仕様の例の為には、OAuth 2.0 Form Post Response Mode[OAuth.Post]を参照してください。 今後、HTML5 postMessage APIやクロスオリジンリソースシェアリング(CORS)を利用することのできるものを含めて、他の仕様で追加のレスポンスモードが定義されることが想定されることを留意してください。

2.2. Multiple-Valued Response Types

複数の値のレスポンスタイプが定義される場合、認可エンドポイントから発行されるレスポンスの為に、以下のエンコーディングルールが適用されることを推奨します。

認可エンドポイントから返却される全てのパラメーターは、同じレスポンスモードを利用すべきです。これは、成功、失敗いずれのレスポンスにも適用されるべきです。

根拠: これはクライアントのパラメーター処理を単純化します。以下で説明しているように、パフォーマンスについての利点もあります。

例えば、レスポンスがフラグメントでエンコードされた部分を含む場合、クライアントのユーザーエージェントコンポーネントはレスポンスの処理を完了する必要があります。🍙 もし新しいクエリパラメーターがクライアントURIに付与された場合、それはユーザーエージェントがクライアントURIを再取得する原因となり、ユーザーエージェントベースのクライアントコンポーネントの継続的ではない操作を引き起こします。🍙 もしフラグメントエンコーディングだけが利用される場合、ユーザーエージェントはフラグメントを処理し、クライアントのホストへ必要に応じてパラメータを(例えばXmlHttpRequestを通じて)送信する、クライアントコンポーネントの再活性化を単純化するでしょう。🍙 したがって、全体をフラグメントエンコーディングすることは、レスポンスの処理において低いレイテンシをもたらします。

3. ID Token Response Type

このセクションでは新しいレスポンスタイプ id_token を OAuth2.0 のセクション8.4の仕様にしたがって登録します。 id_token の用途は、認可サーバーによって解釈されるリソースオーナーのアイデンティティアサーションを提供することです。 そのアサーションは、対象としたオーディエンスを明示しなければいけません。ただし、アサーションの具体的なセマンティクスや、それをどのようにバリデーションするかについては、本ドキュメントでは言及しません。

  • id_token OAuth 2.0 認可リクエストにおいて response_type として提供された場合、成功レスポンスは id_token パラメーターを含む必要があります。 認可サーバーは認可コード、アクセストークン、アクセストークンタイプを、グラントリクエストの成功レスポンスで返却すべきではありません。 もし redirect_uri が提供された場合、ユーザーエージェントは id_token の付与もしくはアクセスの拒否後に、そこにリダイレクトされるべきです。 このリクエストは state パラメーターを含むことができます。その場合、認可サーバーは成功もしくはエラーレスポンスを発行する際に、レスポンスパラメーターとしてその値を返す必要があります。 このレスポンスタイプのデフォルトのレスポンスモードはフラグメントエンコーディングであり、クエリエンコーディングを利用してはいけません。 成功もしくはエラーレスポンスは提供されたレスポンスモードを利用して返却するべきです。 何も指定されなかった場合、デフォルトのレスポンスモードを利用します。

id_token をフラグメントで返却することは、id_token の送信中の漏洩の可能性を減らし、ユーザー(リソースオーナー)のプライバシーに関連するリスクを軽減します。

4. None Response Type

このセクションではレスポンスタイプ none を OAuth2.0 のセクション8.4の仕様にしたがって登録します。 この用途は、パーティが認可サーバーに保護リソースへのアクセス付与の登録をクライアントに代わってリクエストするが、その時点ではクライアントに返却されるクレデンシャルを必要としない場合のユースケースを可能にすることです。 最終的にクライアントがアクセスのクレデンシャルを得る手段は、ここでは指定されません。

1つのシナリオは、マーケットからユーザーがアプリケーションを購入することを期待していて、さらにアプリケーションのインストールの認可を望み、1つのステップでアプリケーションの保護リソースへのアクセスを付与することです。🍙 けれども、ユーザーはその時点で(まだアクティベートされていない)アプリケーションとのインタラクトがないので、認可ステップで同時にアクセスのクレデンシャルを返すことは適切ではありません。🍙

  • none このレスポンスタイプパラメーターが OAuth 2.0 認可リクエストに提供された時、認可サーバーは OAuth 2.0 認可コード、アクセストークン、アクセストークンタイプ、IDトークンをグラントリクエストに対する成功レスポンスで返すべきではありません。 もし redirect_uri が提供された場合、ユーザーエージェントは id_token の付与もしくはアクセスの拒否後に、そこにリダイレクトされるべきです。 このリクエストは state パラメーターを含むことができます。その場合、認可サーバーは成功もしくはエラーレスポンスを発行する際に、レスポンスパラメーターとしてその値を返す必要があります。 このレスポンスタイプのデフォルトのレスポンスモードはフラグメントエンコーディングであり、クエリエンコーディングを利用してはいけません。 成功もしくはエラーレスポンスは提供されたレスポンスモードを利用して返却するべきです。 このレスポンスタイプのためのデフォルトのレスポンスモードはクエリエンコーディングです。 成功もしくはエラーレスポンスは提供されたレスポンスモードを利用して返却するべきです。 何も指定されなかった場合、デフォルトのレスポンスモードを利用します。

このレスポンスタイプ none は、他のレスポンスタイプと組み合わせるべきではありません。

5. Definitions of Multiple-Valued Response Type Combinations

このセクションはそれぞれ個別に登録されたレスポンスタイプ code、 token id_token の組み合わせを定義します。

  • code token この response_type パラメーターが指定された時、成功レスポンスはアクセストークン、アクセストークンタイプ、そして認可コードを含む必要があります。 このレスポンスタイプのためのデフォルトのレスポンスモードはフラグメントエンコーディングであり、クエリエンコーディングは利用してはいけません。 成功もしくはエラーレスポンスは提供されたレスポンスモードを利用して返却するべきです。 何も指定されなかった場合、デフォルトのレスポンスモードを利用します。

  • code id_token この response_type パラメーターが指定された時、成功レスポンスは認可コードと id_token を含む必要があります。 このレスポンスタイプのためのデフォルトのレスポンスモードはフラグメントエンコーディングであり、クエリエンコーディングは利用してはいけません。 成功もしくはエラーレスポンスは提供されたレスポンスモードを利用して返却するべきです。 何も指定されなかった場合、デフォルトのレスポンスモードを利用します。

  • code id_token token この response_type パラメーターが指定された時、成功レスポンスは認可コードと id_token 、そしてアクセストークンとアクセストークンタイプを含む必要があります。 このレスポンスタイプのためのデフォルトのレスポンスモードはフラグメントエンコーディングであり、クエリエンコーディングは利用してはいけません。 成功もしくはエラーレスポンスは提供されたレスポンスモードを利用して返却するべきです。 何も指定されなかった場合、デフォルトのレスポンスモードを利用します。

全てのレスポンスタイプのリクエストは state パラメーターを含むことができます。その場合、認可サーバーは成功もしくはエラーレスポンスを発行する際に、レスポンスパラメーターとしてその値を返す必要があります。

ユーザーエージェントによって発行/受信する参考のリクエスト、レスポンス例(余分な改行は表示用)は、

  GET /authorize?
    response_type=id_token%20token
    &client_id=s6BhdRkqt3
    &redirect_uri=https%3A%2F%2Fclient.example.org%2Fcb
    &state=af0ifjsldkj HTTP/1.1
  Host: server.example.com
  HTTP/1.1 302 Found
  Location: https://client.example.org/cb#
  access_token=SlAV32hkKG
  &token_type=bearer
  &id_token=eyJ0 ... NiJ9.eyJ1c ... I6IjIifX0.DeWt4Qu ... ZXso
  &expires_in=3600
  &state=af0ifjsldkj

7. Security Considerations

クエリストリング内のエンコーディングレスポンスはセキュリティに関連があります。 HTTPリファラーヘッダーは、クエリパラメータを含み、クエリパラメーターにエンコードされた値はサードパーティに漏洩し得ます。 コンフィデンシャルクライアントを利用している場合、認可コードをクエリパラメーターでエンコードするのは安全です。(サードパーティが保持していないクライアントシークレットなしに、利用できないため) ただし、アクセストークンやIDトークンのようなさらにセンシティブな情報は、クエリストリングでエンコードしてはいけません。 デフォルトのレスポンスモードがフラグメントエンコーディングの認可レスポンスパラメーターは、常にクエリエンコーディングを利用すべきではありません。

sliceをrangeでループして取得した値のフィールドを更新しても、元のsliceは更新されない(rangeで取得した値はコピー)

AOJやっててハマったので

package main

import (
    "fmt"
)

type Element struct {
    number int
}

func main() {
    Elementのsliceの場合()
    Elementのポインタのsliceの場合()
}

func Elementのsliceの場合() {
    elements := []Element{}
    for i := 0; i < 2; i++ {
        elements = append(elements, Element{i})
    }
    fmt.Println(elements) // 0 1
    elements[0].number = 10
    fmt.Println(elements) // 10 1

    // これは99にならない
    for _, elem := range elements {
        elem.number = 99
    }
    fmt.Println(elements) // 10 1

    // これは99になる
    for i := 0; i < 2; i++ {
        elements[i].number = 99
    }
    fmt.Println(elements) // 99 99
}

func Elementのポインタのsliceの場合() {
    elements := []*Element{}
    for i := 0; i < 2; i++ {
        elements = append(elements, &Element{i})
    }
    fmt.Println(elements) // 0 1

    // これは99になる
    for _, elem := range elements {
        elem.number = 99
    }
    fmt.Printf("%v %v\n", elements[0].number, elements[1].number)
}

rangeで返ってくる値は要素のコピーになる(=参照が別)のかなと思ってポインタを見てみた感じ、そうっぽい

package main

import (
    "fmt"
)

type Element struct {
    number int
}

func main() {
    Elementのsliceの場合のポインタ()
    Elementのポインタのsliceの場合のポインタ()
}

func Elementのsliceの場合のポインタ() {
    elements := []Element{}
    for i := 0; i < 2; i++ {
        elements = append(elements, Element{i})
    }

    fmt.Println("各要素のポインタ:")
    fmt.Printf("element[0] pointer:%p\n", &elements[0])
    fmt.Printf("element[1] pointer:%p\n", &elements[1])

    fmt.Println("rangeの場合のポインタ:")
    for _, elem := range elements {
        // このポインタは&elements[0]とも&elements[1]とも異なる
        // さらに、ループ内で&elemはどの要素に対するポインタも同じ
        fmt.Printf("element[%v]] pointer:%p\n", elem.number, &elem)
    }

}

func Elementのポインタのsliceの場合のポインタ() {
    elements := []*Element{}
    for i := 0; i < 2; i++ {
        elements = append(elements, &Element{i})
    }
    fmt.Println("各要素のポインタ:")
    fmt.Printf("element[0] pointer:%p\n", &elements[0])
    fmt.Printf("element[1] pointer:%p\n", &elements[1])

    for _, elem := range elements {
        // このポインタは&elements[0]とも&elements[1]と等しい
        fmt.Printf("element[%v]] pointer:%p\n", elem.number, elem)
        // ループ内で&elemはどの要素に対するポインタも同じ(ポインタのポインタになってるはず)
        fmt.Printf("element[%v]] pointer:%p\n", elem.number, &elem)

    }
}

と思ったらtour of goにも書いてあった https://tour.golang.org/moretypes/16

When ranging over a slice, two values are returned for each iteration. The first is the index, and the second is a copy of the element at that index.

channelも同じような感じ

package main

import "fmt"

type Element struct {
    number int
}

func send(c chan Element) {
    for i := 0; i < 3; i++ {
        elem := Element{i}
        fmt.Printf("send: %p\n", &elem)
        c <- elem
    }
    close(c)
}

// send: 0xc000094008
// send: 0xc000094018
// range: 0xc000094000
// range: 0xc000094000
// send: 0xc000018058
// range: 0xc000094000
func main() {
    c := make(chan Element)
    go send(c)

    for val := range c {
        fmt.Printf("range: %p\n", &val)
    }
}

関数に渡した場合にコピーになる奴でもちょっとハマったんだよな

どうするか

  • インデックスでループしてインデックスでアクセス
  • rangeでループしてインデックスでアクセス
  • ポインタの配列にする

あたり?

2019年10月にやったこと

2019年10月にやったこと

アルゴリズムの本読みながら問題解いてたら終わったのであんまり書くことがない。 なぜかFinal: OpenID Authentication 2.0 - 最終版を読んだりしていた。

英語

レアジョブの先生に「ディズニーの映画を見ろ。1週間同じやつをみ続けろ。1ヶ月で4本だ。」と言われ、アラジンを7回みた。

その後TOEICの結果が返ってきて、思ったよりだいぶ良い結果だったので、その勢いで勉強が停滞している。

プログラミングコンテスト攻略のためのアルゴリズムとデータ構造

いい加減アルゴリズムをやるかと思って初めて、178ページまでやった。なんとなくGoでやっている。 Javaのコレクションライブラリって便利なんだな・・・とかジェネリクス欲しいな・・・とかそういう気持ちになりつつも面白いのでちまちま解いている。

体系的に学ぶ 安全なWebアプリケーションの作り方 第2版 脆弱性が生まれる原理と対策の実践

半額だったので勢いで買ってしまった。 3割くらい読んだが今の所別に読まなくてもよかったかなあという気持ちが若干出てきたがさらい直しておくべき分野なのはそうなので、ざらっと読む。

Final: OpenID Connect Core 1.0 incorporating errata set 1

とりあえず一通り読んだ。これとか全然知らなかった。

その他

idance.connpass.com

これにいったりしていた。WebAuthnのドキュメントを読みたいが時間がない。

OpenID Connect Session Management 1.0のブログを書きたいがこれも時間がない。

これからやること

  • プログラミングコンテスト攻略のためのアルゴリズムとデータ構造
    • 読んでる
  • ElectronかWebアプリ(サーバなし)でNature RemoのPC用クライアントを作る
    • なぜか俺は音声やスマホよりもPCで操作したい時がたまにある
    • あとはSPAとJSの学習用に
  • Goならわかるシステムプログラミング
  • Kotlinがらみなんか
    • Kotlinインアクションとか
  • 英語
  • 中国語
    • Toeic900超えるまで止める予定
  • ここ最近ほとんど業務でプログラミングをしなくなって、調整とかレビューとかばっかしてるがかなり雰囲気でやってるので、マネジメント系の本を読もうとしている
    • なにがいいのかよくわからん
  • Effective Go - The Go Programming Language
  • パタヘネ
  • OAuth2.0のクライアント書く

2019年9月にやったこと

引き続きSpring Security・OAuth2.0、OIDCあたりをごにょごにょしながらなぜかelmに入門していた。

2019年9月にやったこと

英語

Santa Toeicというアプリで遊んでいた。 簡単に言えばAIが選んだユーザーの苦手なToeicの模擬試験をガンガン解くためのアプリと、AIが判断した弱いジャンルの授業(Youtubeの動画)をおすすめしてくるアプリ。

前者は面白いので結構やった。たまにバグるが確かに苦手な問題がよく出てくる気がする。後者はあまりにもToeicに特化した授業感があって途中で受けるのをやめた。問題文に似てる単語が出てくる回答の選択肢は間違いである、みたいな話をすごく頻繁にしてくる感じ。

最初に何問か問題を解いてToeicの点数予測をしてくれる奴が面白い。最高得点770で、予測が750だったので結構すごいなと思った。

あとはRarejobの講師に勧められたのでなぜかアラジンの映画を繰り返し見ていた。

あとToeicを受けた。

雰囲気でOAuth2.0を使っているエンジニアがOAuth2.0を整理して理解できる本

読んだ。よかったので読書感想文を書いた。

【書評】雰囲気でOAuth2.0を使っているエンジニアがOAuth2.0を整理して、手を動かしながら学べる本 #技術書典7 | DevelopersIO

PKCEの復習とかできてよかった。

OAuth、OAuth認証、OpenID Connectの違いを整理して理解できる本

読んだ。読書感想文を書こうとしている。OAuth 2.0による認証の話から始まるのが良い。

OAuth2.0のクライアントアプリケーションを書く

とりあえず動くところまで書いた。GUIにしようとしている。

基礎からわかる Elm

クライアントアプリケーションのGUIを作るのに、なんとなくElmに入門しようと思ってはじめに · An Introduction to Elmを読んでた。 面白いがちょくちょくわからんので本を買ってみたが、この本はすごくわかりやすかった。 ガイド読んで諦めた人にすすめたい。

なんとなくElm書いてる

GitHub - inabajunmr/elm-playground: playground for elm

とりあえずTODOリスト

まるばつゲームを書いている

終わったらテトリスを書きたい気持ち

Real World HTTP ―歴史とコードに学ぶインターネットとウェブ技術

読んだ。パイプラインの話とか面白かったが、だいぶ記憶から消えたのでまた読むかも。

FIDO2

なんとなくドキュメント読んでる。

ブログ書いたり

dev.classmethod.jp

dev.classmethod.jp

これからやること

  • プログラミングコンテスト攻略のためのアルゴリズムとデータ構造
    • 読む
  • ElectronかWebアプリ(サーバなし)でNature RemoのPC用クライアントを作る
    • なぜか俺は音声やスマホよりもPCで操作したい時がたまにある
      • あとはSPAとJSの学習用に
        • 読書会プラットフォームの後
  • Goならわかるシステムプログラミング
  • Kotlinがらみなんか
    • Kotlinインアクションとか
  • 英語
  • 中国語
    • Toeic900超えるまで止める予定
  • ここ最近ほとんど業務でプログラミングをしなくなって、調整とかレビューとかばっかしてるがかなり雰囲気でやってるので、マネジメント系の本を読もうとしている
    • なにがいいのかよくわからん
  • Effective Go - The Go Programming Language
  • パタヘネ
  • OAuth2.0のクライアント書く

2019年7月、8月にやったこと

引っ越したらもろもろ適当すぎてインターネットがない期間がかなりあって泣いていた。 引き続きSpring Security・OAuth2.0、OIDCあたりをごにょごにょしていた。

英語

rarejobをちょっとだけやった あとはiknowを2ヶ月で20時間くらいやって、あとはTOEICの公式問題集のディクテーションをしていた

いい加減TOEIC800超えたいのでとりあえずTOEIC特化でもいいや感がでてきた

OAuth徹底入門 セキュアな認可システムを適用するための原則と実践

よみおわった セキュアにするにはどうする?みたいな話がいっぱいあってよかった PKCEとか

OAuth2.0のクライアントアプリケーションを書く

なんとなく書き始めた テストどうするか悩んでいる

Clean Architecture 達人に学ぶソフトウェアの構造と設計

なんか半分くらいよんだ

レイヤーが上のほうな感じの話になってきてすごいゆっくりペースになっている

その他

buildersconにいったり

builderscon tokyo 2019に行ってきた - チョキチョキかにさん

あとはなんかブログをぽつぽつかいていた

dev.classmethod.jp

dev.classmethod.jp

dev.classmethod.jp

dev.classmethod.jp

dev.classmethod.jp

dev.classmethod.jp

dev.classmethod.jp

qiita.com

これからやること

builderscon tokyo 2019に行ってきた

行ってきた

見たセッションとか

ブロックチェーン時代の認証 - builderscon tokyo 2019

ブロックチェーンによって、データをサービス提供者が一元管理するのではなく、あくまでユーザー側がデータを「保持」できる世界観になる、という話が印象的だった カードゲームなら、サービスを介さないでユーザー間だけでカードの交換できるとか

あとはEthereum ウォレットのAPIの話とかブラウザの対応とかの話とか

認証の話でいうと、デバイス秘密鍵管理してメッセージに署名して投げる的な流れがFIDOと同じような感じで相性が良くてウォレットを使ってるとパスワードレス認証がいい感じに実装できる話などがあった

コンパイラをつくってみよう - builderscon tokyo 2019

これはすごく良かったので別途ブログに書いた。

[builderscon 2019 レポート] コンパイラを作ってみよう #builderscon | DevelopersIO

コンパイラを書くの、自分とは関係ない世界のなんかすごい人じゃないと難しいやつ、みたいな印象が雑にあったが少なくとも書き始めることはできるなーという気持ちになった。 ライブコーディング中のバグに対する指摘が客席から飛ぶのが笑えた。

WebAuthn/FIDOのUX徹底解説 ~実サービスへの導入イメージを添えて~ - builderscon tokyo 2019

WebAuthnとFIDOの関係性とか

WebAuthn、色々なサービスが対応してきてるなーーーそろそろ真面目に見なきゃなーーーという感じになっている

ウォレットアプリ「Kyash」の先 〜「Kyash Direct」のアーキテクチャ〜 - builderscon tokyo 2019

リクエストをメッセージバスに送って、非同期でマイクロサービスが諸々処理して、全部完了したら同期的にレスポンスを返すアーキテクチャの話とか

面白いし正しい感じはするけど、これ真面目に運用するとすげー大変そうだなとか何人くらいでやってんだろとか気になった

OSS Security the hard way - builderscon tokyo 2019

Ruby脆弱性の報告がきてそのあとどうするかのフローの話とか

パッチをオープンな場で管理できなくて辛いとか

横串の別のコードの脆弱性対応のパッチの著作権の問題を解決するためにまだパッチを見てない人が修正対応に当たった話とか

Building, and Upkeeping Super Kamiokande - builderscon tokyo 2019

2割くらいしか理解できてない気がするがすごい面白かった

東海村から加速器で飛ばしたニュートリノを飛騨のスーパーカミオカンデで観測するのに、時刻を同期する必要があってめちゃくちゃ精度がいて、GPSで時刻同期するんだけどGPSの衛星が複数あって衛星間のずれをよしなにするために衛星を認識できるレシーバーを自作する話とか

ネットワークのケーブルの長さが違うと転送にかかる時間がまちまちになるから必要な長さにかかわらず全部同じ長さのケーブルにしたりとか

パケットロスしたりパケットを勝手に補正したりしないスイッチを選定する話とか

ベテルギウス超新星爆発で飛んで来るニュートリノの数がスパイクする話とか

入門サービスメッシュ - builderscon tokyo 2019

どう行った経緯でサービスメッシュが作られたかとか

サービスメッシュを使うのにコンテナでアプリがデプロイされてる必要がない話とか

istioとenvoyの話とかプロキシとかサービスメッシュの実装色々の話とか

経緯の話があると改めて何をするために存在してるのかが認識できて良いなと思った

自動更新プランの更新直前にイングリッシュベルを解約するために3510円かかった話

やる気がなくなったのでイングリッシュベルを解約しようとしてQAを見ていたら

https://english-bell.com/ja/guide/anwser.html?id=86

f:id:inabajunmr:20190725175948p:plain

とのことだったので、解約を依頼したところ、

  • レッスンを消化しきった上で次回自動更新日 の5営業日前に連絡しろ
  • もう次回更新日まで5営業日を切っているので、今回の自動更新は止められない

と返信があった。

なんだそのルールは、と思ってページを見ていたところ

https://english-bell.com/ja/info2/fee2.html f:id:inabajunmr:20190725180441p:plain

という記載があった。自動更新を契約する際の導線にその記載がないのはどうなんだと思ったが、めんどくさいので素直に解約することにした。

が、そのままレッスンを消化して自動更新がかかった後に再度解約依頼をする場合、今のプランの更新料がそのままかかってしまうので、一旦レッスンを全て消化してから自動更新・月5回プランに変更した。ここで3510円かかった。さようならイングリッシュベル。パスワードを平文で保存するのはよくないのではないか。