[VB.NET] REST API⑭ OAuth2.0 認証#1(APIに認証機能を実装)

2021年10月7日

ASP.NET MVC ビューに複数のパラメーターを渡すで作成したサンプルを改造して、
Web APIにOAuth2.0認証の機能を実装してみましょう。
※ VisualStudio2019
※ 当方のOAuth認証の知識が不足しているため、あくまで参考程度にとどめてください。

パッケージのインストール

まずはNugetから、必要なパッケージをインストールしていきます。
インストールするのは以下の4つのOwin関連のパッケージです。

Microsoft.Owin.Host.SystemWeb
Microsoft.Owin.Security.OAuth
Microsoft.AspNet.WebApi.Owin
Microsoft.AspNet.Identity.Owin

Owinというのは、「Open Web Interface for .NET」の略で、「HTTPを処理するサーバとアプリケーションを抽象化するためのインターフェイスの仕様」らしいです。
正直いまいちピンきていませんが、とりあえず動くサンプルを作っていきます。

「ツール」>「Nugetパッケージマネージャー」>「ソリューションのNugetパッケージの管理」をクリックします。

パッケージの管理画面が開いたら、「参照」タブを選択して、検索ワードに「Owin」と入力します。
すると、関連するパッケージが表示されます。

まずは、「Microsoft.Owin.Host.SystemWeb」をインストールします。

パッケージを選択して、インストールしたい「プロジェクト」にチェックを付けて「インストール」をクリックすればOKです。インストール中に「変更のプレビュー」とか「ライセンスへの同意」のメッセージが表示されたら、「OK」とか「同意」を押してください。

続けて「Microsoft.Owin.Security.OAuth」もインストールします。

続けて「Microsoft.Owin.Host.SystemWeb」もインストールします。

続けて「Microsoft.AspNet.Identity.Owin」もインストールします。
インストールするものはこれで終わりです。
次はいよいよ実装をしていきます。

WebApiConfig に認証方法を追加する

ソリューションエクスプローラで、 「App_Start」>「WebApiConfig.vb」を開き、「Bearer Token」認証のみを使用するようにコードを追加します。

コード

Imports System
Imports System.Collections.Generic
Imports System.Linq
Imports System.Web.Http

Public Module WebApiConfig
    Public Sub Register(ByVal config As HttpConfiguration)
        ' Web API の設定およびサービス

        'Bearer token 認証
        config.SuppressDefaultHostAuthentication()
        config.Filters.Add(New HostAuthenticationFilter(Microsoft.Owin.Security.OAuth.OAuthDefaults.AuthenticationType))


        ' Web API ルート
        config.MapHttpAttributeRoutes()

        config.Routes.MapHttpRoute(
            name:="DefaultApi",
            routeTemplate:="api/{controller}/{id}",
            defaults:=New With {.id = RouteParameter.Optional}
        )
    End Sub
End Module

OAuth認証のプロバイダーを追加

次に、OAuth認証をするためのプロバイダークラスを追加します。

注意点として、このサンプルではASP.NETフォルダーから「App_Code」フォルダを作って、その中にコードファイルを作成しています。
この方法だと、コードファイルのビルドアクションが「コンテンツ」で作成されるため、追加後に「ビルド」に変更する必要があります。この作業をしないと、追加したクラスを別のモジュールから参照できないので注意してください。

ソリューションエクスプローラでプロジェクトを右クリックし、
「追加」>「ASP.NETフォルダーの追加」>「App_Code」をクリックして、フォルダを追加します。

ソリューションエクスプローラで、 追加した「App_Code」を右クリックして、「追加」>「クラス」をクリックします。

クラス名を入力し、「追加」をクリックします。
このサンプルでは、「OAuthProvider」という名前にしています。

追加した「OAuthProvider」クラスのビルドアクションを変更します。

ソリューションエクスプローラーで、 「OAuthProvider」 を右クリックし、プロパティを表示します。

プロパティウィンドウで、「ビルドアクション」を「コンパイル」に変更します。

「OAuthProvider」 にカスタム認証機能を実装します。

「OAuthAuthorizationServerProvider」を継承し、メソッドをオーバーライドします。
「GrantResourceOwnerCredentials」では、ユーザーとパスワードの確認をしています。
このサンプルではコード内でユーザーとパスワードを固定でチェックしています。
ユーザーとパスワードが一致しない場合は要求を拒否します。

コード

Imports System.Security.Claims
Imports System.Threading.Tasks
Imports Microsoft.Owin.Security.OAuth


Public Class OAuthProvider
    Inherits OAuthAuthorizationServerProvider

    ''' <summary>
    ''' クライアント有効チェック
    ''' </summary>
    ''' <param name="context"></param>
    ''' <returns></returns>
    Public Overrides Function ValidateClientAuthentication(ByVal context As OAuthValidateClientAuthenticationContext) As Task
        context.Validated()
        Return Task.FromResult(0)
    End Function

    ''' <summary>
    ''' ユーザー認証
    ''' </summary>
    ''' <param name="context"></param>
    ''' <returns></returns>
    Public Overrides Function GrantResourceOwnerCredentials(ByVal context As OAuthGrantResourceOwnerCredentialsContext) As Task

        'テスト用なのでユーザー/パスワードは固定
        Dim user As String = "user1"
        Dim pass As String = "password1"

        'ユーザー/パスワードをチェック
        If context.UserName = user AndAlso context.Password = pass Then

            'identityを生成
            Dim identity = New ClaimsIdentity(context.Options.AuthenticationType)
            identity.AddClaims({New Claim(ClaimTypes.GivenName, context.UserName), New Claim(ClaimTypes.Role, "Admin")})
            context.Validated(identity)
        Else
            '拒否する
            context.Rejected()
        End If

        Return Task.FromResult(0)
    End Function
End Class

OAuthのスタートアップを追加する

続いて、OAuthのスタートアップクラスを追加します。
ソリューションエクスプローラーで、プロジェクトを右クリックし、「追加」>「クラス」をクリックします。

クラス名を入力し、「追加」をクリックします。
このサンプルでは、「Startup」という名前にしています。

「Startup」にOAuthベアラートークンを使用するためのコードを記述します。

「/Token」にアクセスすることで「OAuthProvider」が呼び出され、アクセストークンが取得できるようになります。
「AccessTokenExpireTimeSpan」でアクセストークンの有効期限を1分にしているのは、有効期限切れの時の動作をテストしたかったためです。デフォルトは20分のようです。

コード

Imports Microsoft.Owin
Imports Microsoft.Owin.Security.OAuth
Imports Owin

<Assembly: OwinStartup(GetType(Startup))>

Public Class Startup
    Public Sub Configuration(ByVal app As IAppBuilder)
        app.UseOAuthBearerTokens(New OAuthAuthorizationServerOptions With {
            .TokenEndpointPath = New PathString("/Token"),
            .Provider = New OAuthProvider,
            .AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(1),
            .AllowInsecureHttp = True
        })
    End Sub
End Class

コントローラーに認証機能をつける

APIのコントローラー(ApiTestController)に、認証機能をつけます。
やり方は簡単で、

<Authorize>
Public Class ApiTestController

のように、クラスに「Authorize」属性をつけるだけです。

実行して確認する

さっそく実行してみましょう。実行方法はこちらを参照してください。

アドレスバーに「/ViewTest/index」と入力してEnterキーを押してページを移動してみてください。
ユーザー認証もトークンの取得もせずにリクエストしているので、エラーメッセージが表示されリクエストが拒否されるようになりました。

OAuth2.0認証の機能を実装 をAPIに実装してみました。
Microsoftのパッケージをインストールすれば楽に実装できるのが良いですね。ありがたいことです。

※繰り返しになりますが、ここで紹介したのはあくまでサンプルレベルのものですので注意してください。
難しいことはMicrosoftのパッケージがやってくれてるようですが、これで十分かどうかは恥ずかしながら私の知識では判断できません。

次回は、ビューからこのAPIに認証トークンのリクエストをおこなってみます。