---
# System prepended metadata

title: Net Core 使用apple登入(openId 套件)
tags: [apple, .net core, openId, 第三方登入]

---

# Net Core 使用apple登入(openId 套件)
###### tags: `apple` `.net core` `openId` `第三方登入`

## 方法2:使用套件(openid)

[範例原文](https://www.scottbrady91.com/openid-connect/implementing-sign-in-with-apple-in-aspnet-core)


#### Apple 開發者帳戶設定

[官方設定說明](https://help.apple.com/developer-account/#/dev1c0e25352)
創建為“使用 Apple 登錄”配置的 App ID
創建為“使用 Apple 登錄”配置的服務 ID（包括經過驗證的網域和redirect URIs）
創建一個可由您創建的服務 ID 使用的密鑰(p8)
不幸的是，redirect URIs不能使用 localhost ，但是可以在本機上修改測試用網域` http://dino.com/signin-apple`
（在C:\Windows\System32\drivers\etc\hosts 中可以加入設定使 127.0.0.1 可以對應到 dino.com）
`127.0.0.1     dino.com`

修改專案的開啟網址(localhost:5000 → dino.com(443 port))

`.\Properties\launchSettings.json`

```
"projectname": {
  "commandName": "Project",
  "launchBrowser": true,
  "environmentVariables": {
    "ASPNETCORE_ENVIRONMENT": "Development",
    "ASPNETCORE_HOSTINGSTARTUPASSEMBLIES": "Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation"
  },
  "dotnetRunMessages": "true",
  "applicationUrl": "https://dino:443;http://localhost:5000"
}
```



#### Startup


```
            services.AddAuthentication(options =>
            {
                options.DefaultScheme = "cookie";
                options.DefaultChallengeScheme = "apple";
            }).AddCookie("cookie").AddOpenIdConnect("apple", options =>
            {
                options.Authority = "https://appleid.apple.com"; // disco doc: https://appleid.apple.com/.well-known/openid-configuration
                options.ClientId = ""; // Service ID
                options.CallbackPath = "/signin-apple"; // corresponding to your redirect URI
                options.ResponseType = "code id_token"; // hybrid flow due to lack of PKCE support
                options.ResponseMode = "form_post"; // form post due to prevent PII in the URL
                options.DisableTelemetry = true;
                options.Scope.Clear(); // apple does not support the profile scope
                options.Scope.Add("openid");
                options.Scope.Add("email");
                options.Scope.Add("name");
                // custom client secret generation - secret can be re-used for up to 6 months
                options.Events.OnAuthorizationCodeReceived = context =>
                {
                    context.TokenEndpointRequest.ClientSecret = TokenGenerator.CreateNewToken();
                    return Task.CompletedTask;
                };
                options.UsePkce = false;
            });
```
#### Controller
    
這邊需要注意當有多個第三方登入同時存在時AuthenticateAsync需要指定明確OpenIdConnect的名稱，否則會出現錯誤。
EX: HttpContext.AuthenticateAsync("apple")

```
[AllowAnonymous]
[HttpGet]
public async Task<IActionResult> TestChallenge()
{
    var result = await HttpContext.AuthenticateAsync("apple");
    var cccc = HttpContext.User.Claims;
    if (result.Succeeded)
    {
        return RedirectToAction("Index");
    } 

    return Challenge("apple");
}
```

#### View

只要可以連到對應controller的Action即可，方法不限制只能使用`<a>`  也可以傳入參數進行登入後的判斷。


``` 
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="TestChallenge">Sign In with Apple</a>
```

#### Helper

```
 public static class TokenGenerator
    {
        public static string CreateNewToken()
        {
            const string iss = "6YPRZBQSZE"; // your account's team ID found in the dev portal
            const string aud = "https://appleid.apple.com";
            const string sub = "com.icirround.ContactBook.AppleSignIn"; // same as client_id
            var now = DateTime.UtcNow;

            // contents of your .p8 file
            const string privateKey = "MIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQghg+OSFvegWxnlR1G0DPK9L9yyjf6xYg/ek15jRN0OFugCgYIKoZIzj0DAQehRANCAAQW40tJJig10T4SaGDB3ePNH3ymGV4VCf2fuO/qDMVn83XTi9kfXbP6DKREP8D8pBudopETjPeALI5/zh0sT7sM";
            var ecdsa = ECDsa.Create();
            ecdsa?.ImportPkcs8PrivateKey(Convert.FromBase64String(privateKey), out _);
            var handler = new JsonWebTokenHandler();
            var result = handler.CreateToken(new SecurityTokenDescriptor
            {
                Issuer = iss,
                Audience = aud,
                Claims = new Dictionary<string, object> { { "sub", sub } },
                Expires = now.AddMonths(1), // expiry can be a maximum of 6 months - generate one per request or re-use until expiration
                IssuedAt = now,
                NotBefore = now,
                SigningCredentials = new SigningCredentials(new ECDsaSecurityKey(ecdsa), SecurityAlgorithms.EcdsaSha256)
            });

            return result;
        }
    }
```

