Adicionar autenticação ao seu aplicativo iOS (Swift)
Este guia assume que você criou um Aplicativo do tipo "Native app" no Console de Administração.
Instalação
The minimum supported iOS version of Logto Swift SDK is iOS 13.
Logto Swift SDK comes in two major versions:
- v1: Opens the sign-in experience in an embedded WebView, which is required by the native social plugin targets, but does not support passkey sign-in (WebView does not support WebAuthn, the underlying standard of passkeys).
- v2 (beta): Opens the sign-in experience in
ASWebAuthenticationSession(the system browser), which unlocks passkey sign-in and shares the browser session. Note that v2 removes the native social plugin targets; social connectors still work through the browser. If you depend on the native WeChat or Alipay SDK handoff, stay on v1.
This guide covers both versions. Choose your version in the tabs below, and the choice will be kept in sync throughout this guide.
Use the following URL to add Logto SDK as a dependency in Swift Package Manager.
https://github.com/logto-io/swift.git
Since Xcode 11, you can directly import a Swift package w/o any additional tool.
When Xcode asks for the package version, choose the version you want to integrate:
- v2 (beta)
- v1
v2 is released as 2.0.0-beta.x prereleases until GA. Use 2.0.0-beta.1 or the latest 2.0.0-beta.x prerelease as the version. During beta, we recommend selecting the prerelease explicitly instead of relying on a normal version range to pick it automatically.
If you use Package.swift directly:
.package(url: "https://github.com/logto-io/swift.git", exact: "2.0.0-beta.1")
Use the latest v1 release as the stable line. The latest v1 version is 1.2.0.
If you use Package.swift directly:
.package(url: "https://github.com/logto-io/swift.git", from: "1.2.0")
We do not support Carthage and CocoaPods at the time due to some technical issues.
Carthage
Carthage needs a xcodeproj file to build. We will try to find a workaround later.
CocoaPods
CocoaPods does not support local dependency and monorepo, thus it's hard to create a .podspec for this repo.
Integração
Inicializar LogtoClient
Inicie o cliente criando uma instância de LogtoClient com um objeto LogtoConfig.
import Logto
import LogtoClient
let config = try? LogtoConfig(
endpoint: "<your-logto-endpoint>", // Ex.: http://localhost:3001
appId: "<your-app-id>"
)
let client = LogtoClient(useConfig: config)
Por padrão, armazenamos credenciais como Token de ID (ID Token) e Token de atualização (Refresh Token) no Keychain. Assim, o usuário não precisa fazer login novamente quando retornar.
Para desativar esse comportamento, defina usingPersistStorage como false:
let config = try? LogtoConfig(
// ...
usingPersistStorage: false
)
Implementar login e logout
Antes de entrarmos nos detalhes, aqui está uma visão geral rápida da experiência do usuário final. O processo de login pode ser simplificado da seguinte forma:
- Seu app invoca o método de login.
- O usuário é redirecionado para a página de login do Logto. Para aplicativos nativos, o navegador do sistema é aberto.
- O usuário faz login e é redirecionado de volta para seu app (configurado como o URI de redirecionamento).
Sobre o login baseado em redirecionamento
- Este processo de autenticação segue o protocolo OpenID Connect (OIDC), e o Logto aplica medidas de segurança rigorosas para proteger o login do usuário.
- Se você tiver vários aplicativos, pode usar o mesmo provedor de identidade (Logto). Uma vez que o usuário faz login em um aplicativo, o Logto completará automaticamente o processo de login quando o usuário acessar outro aplicativo.
Para saber mais sobre a lógica e os benefícios do login baseado em redirecionamento, veja Experiência de login do Logto explicada.
Configure redirect URI
- v2 (beta)
- v1
Vamos mudar para a página de detalhes do Aplicativo no Logto Console. Adicione um URI de redirecionamento io.logto.app://callback e clique em "Salvar alterações".

In v2, the sign-in experience opens in ASWebAuthenticationSession (the system browser), and the redirect is routed back to your app through OS-level callback matching. For a custom scheme redirect URI such as io.logto.app://callback, register only the scheme part (io.logto.app) in your app's Info.plist, then add the full redirect URI to your Logto application's Redirect URIs.
In Xcode, open your app target, select Info, expand URL Types, and add one entry with io.logto.app in URL Schemes. If you edit Info.plist directly, add:
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLName</key>
<string>io.logto.app</string>
<key>CFBundleURLSchemes</key>
<array>
<string>io.logto.app</string>
</array>
</dict>
</array>
For the browser flow in v2, you do not need to call LogtoClient.handle(url:); that plugin handoff API was removed with the embedded WebView flow.
Use Universal Links instead of a custom scheme?
You can also use an HTTPS redirect URI such as https://example.com/callback:
- Add the Associated Domains capability to your app.
- Configure
webcredentials:example.comsoASWebAuthenticationSessioncan match HTTPS callbacks on iOS 17.4 and newer. - If the same URL should also open your app as a Universal Link outside the authentication session, configure
applinks:example.comand host a validapple-app-site-associationfile for the domain and path. - Add the HTTPS URI to your Logto application's Redirect URIs.
- Pass the same URI to
signInWithBrowser.
On iOS 17.4 and newer, the SDK uses ASWebAuthenticationSession's HTTPS callback matching API so HTTPS redirects can automatically complete and dismiss the session. On older iOS versions, the authorization request can still use the HTTPS redirect URI, but the session may not close automatically unless your app handles the Universal Link callback itself. Keep a custom scheme redirect as a compatibility option if you need automatic completion on older iOS versions.
Vamos mudar para a página de detalhes do Aplicativo no Logto Console. Adicione um URI de redirecionamento io.logto://callback e clique em "Salvar alterações".

The Redirect URI in iOS SDK is only for internal use. There's NO NEED to add a Custom URL Scheme until a connector asks.
Sign-in and sign-out
Antes de chamar .signInWithBrowser(redirectUri:), certifique-se de que você configurou corretamente o URI de redirecionamento no Admin Console.
- v2 (beta)
- v1
In v2, client.signOut(postLogoutRedirectUri:) performs a complete sign-out: it clears the local credentials, revokes the refresh token, and ends the Logto session by opening the end session endpoint in the system browser. The browser then navigates back to your app through the post sign-out redirect URI. Before using it, switch to the application details page of Logto Console, add the post sign-out redirect URI io.logto.app://signed-out and click "Save changes". The post sign-out redirect URI can use the same custom scheme you registered for sign-in.
For example, in a SwiftUI app:
struct ContentView: View {
@State var isAuthenticated: Bool
private let redirectUri = "io.logto.app://callback"
private let postLogoutRedirectUri = "io.logto.app://signed-out"
init() {
isAuthenticated = client.isAuthenticated
}
var body: some View {
VStack {
if isAuthenticated {
Button("Sign Out") {
Task { [self] in
let error = await client.signOut(postLogoutRedirectUri: postLogoutRedirectUri)
if let error = error {
print(error)
return
}
isAuthenticated = false
}
}
} else {
Button("Sign In") {
Task { [self] in
do {
try await client.signInWithBrowser(redirectUri: redirectUri)
isAuthenticated = true
} catch let error as LogtoClientErrors.SignIn {
// error occurred during sign in
} catch {
// other errors
}
}
}
}
}
}
}
- You can also call
client.signOut()without a post sign-out redirect URI. No Console configuration is needed in this case: the browser shows the Logto sign-out page, and the user returns to the app by dismissing it manually. - If no UI context is available, you can call
client.clearCredentials()to clear the local credentials and revoke the refresh token. Note that this keeps the Logto session in the browser, so the nextsignInWithBrowsermay silently sign the user back in through that session.
You can use client.signInWithBrowser(redirectUri:) to sign in the user and client.signOut() to sign out the user.
For example, in a SwiftUI app:
struct ContentView: View {
@State var isAuthenticated: Bool
init() {
isAuthenticated = client.isAuthenticated
}
var body: some View {
VStack {
if isAuthenticated {
Button("Sign Out") {
Task { [self] in
await client.signOut()
isAuthenticated = false
}
}
} else {
Button("Sign In") {
Task { [self] in
do {
try await client.signInWithBrowser(redirectUri: "io.logto://callback")
isAuthenticated = true
} catch let error as LogtoClientErrors.SignIn {
// error occurred during sign in
} catch {
// other errors
}
}
}
}
}
}
}
Ponto de verificação: Teste seu aplicativo
Agora, você pode testar seu aplicativo:
- Execute seu aplicativo, você verá o botão de login.
- Clique no botão de login, o SDK iniciará o processo de login e redirecionará você para a página de login do Logto.
- Após fazer login, você será redirecionado de volta para seu aplicativo e verá o botão de logout.
- Clique no botão de logout para limpar o armazenamento de tokens e sair.
Obter informações do usuário
Exibir informações do usuário
Para exibir as informações do usuário, você pode usar o método client.getIdTokenClaims(). Por exemplo, em um aplicativo SwiftUI:
struct ContentView: View {
@State var isAuthenticated: Bool
@State var name: String?
init() {
isAuthenticated = client.isAuthenticated
name = try? client.getIdTokenClaims().name
}
var body: some View {
VStack {
if isAuthenticated {
Text("Bem-vindo, \(name)")
} else {
Text("Por favor, faça login")
}
}
}
}
Solicitar reivindicações adicionais
Você pode perceber que algumas informações do usuário estão faltando no objeto retornado de client.getIdTokenClaims(). Isso ocorre porque OAuth 2.0 e OpenID Connect (OIDC) são projetados para seguir o princípio do menor privilégio (PoLP), e o Logto é construído com base nesses padrões.
Por padrão, reivindicações limitadas são retornadas. Se você precisar de mais informações, pode solicitar escopos adicionais para acessar mais reivindicações.
Uma "reivindicação (Claim)" é uma afirmação feita sobre um sujeito; um "escopo (Scope)" é um grupo de reivindicações. No caso atual, uma reivindicação é uma informação sobre o usuário.
Aqui está um exemplo não normativo da relação escopo - reivindicação:
A reivindicação "sub" significa "sujeito (Subject)", que é o identificador único do usuário (ou seja, ID do usuário).
O Logto SDK sempre solicitará três escopos: openid, profile e offline_access.
Para solicitar escopos adicionais, você pode passá-los para o objeto LogtoConfig. Por exemplo:
let config = try? LogtoConfig(
endpoint: "<your-logto-endpoint>", // Ex.: http://localhost:3001
appId: "<your-app-id>",
scopes: [
UserScope.Email.rawValue,
UserScope.Phone.rawValue,
]
)
Então você pode acessar as reivindicações adicionais no valor de retorno de client.getIdTokenClaims():
let claims = try? client.getIdTokenClaims()
// Agora você pode acessar as reivindicações adicionais `claims.email`, `claims.phone`, etc.
Reivindicações que precisam de solicitações de rede
Para evitar o inchaço do Token de ID (ID token), algumas reivindicações requerem solicitações de rede para serem buscadas. Por exemplo, a reivindicação custom_data não está incluída no objeto do usuário, mesmo que seja solicitada nos escopos. Para acessar essas reivindicações, você pode usar o método client.fetchUserInfo():
let userInfo = try? client.fetchUserInfo()
// Agora você pode acessar a reivindicação `userInfo.custom_data`
Escopos e reivindicações
Logto utiliza as convenções de escopos e reivindicações (scopes and claims) do OIDC para definir os escopos e reivindicações para obtenção de informações do usuário a partir do token de ID (ID token) e do endpoint userinfo do OIDC. Tanto "escopo (Scope)" quanto "reivindicação (Claim)" são termos das especificações do OAuth 2.0 e OpenID Connect (OIDC).
Para reivindicações padrão do OIDC, a inclusão no token de ID é estritamente determinada pelos escopos solicitados. Reivindicações estendidas (como custom_data e organizations) podem ser configuradas adicionalmente para aparecer no token de ID através das configurações de Token de ID personalizado.
Aqui está a lista de escopos suportados e as reivindicações correspondentes:
Escopos OIDC padrão
openid (padrão)
| Nome da reivindicação | Tipo | Descrição |
|---|---|---|
| sub | string | O identificador único do usuário |
profile (padrão)
| Nome da reivindicação | Tipo | Descrição |
|---|---|---|
| name | string | O nome completo do usuário |
| username | string | O nome de usuário do usuário |
| picture | string | URL da foto de perfil do usuário final. Esta URL DEVE se referir a um arquivo de imagem (por exemplo, um arquivo de imagem PNG, JPEG ou GIF), em vez de uma página da Web contendo uma imagem. Observe que esta URL DEVE referenciar especificamente uma foto de perfil do usuário final adequada para exibição, em vez de uma foto arbitrária tirada pelo usuário final. |
| created_at | number | Momento em que o usuário final foi criado. O tempo é representado como o número de milissegundos desde a época Unix (1970-01-01T00:00:00Z). |
| updated_at | number | Momento em que as informações do usuário final foram atualizadas pela última vez. O tempo é representado como o número de milissegundos desde a época Unix (1970-01-01T00:00:00Z). |
Outras reivindicações padrão incluem family_name, given_name, middle_name, nickname, preferred_username, profile, website, gender, birthdate, zoneinfo e locale também serão incluídas no escopo profile sem a necessidade de solicitar o endpoint userinfo. Uma diferença em relação às reivindicações acima é que essas reivindicações só serão retornadas quando seus valores não forem vazios, enquanto as reivindicações acima retornarão null se os valores estiverem vazios.
Diferente das reivindicações padrão, as reivindicações created_at e updated_at usam milissegundos em vez de segundos.
email
| Nome da reivindicação | Tipo | Descrição |
|---|---|---|
string | O endereço de email do usuário | |
| email_verified | boolean | Se o endereço de email foi verificado |
phone
| Nome da reivindicação | Tipo | Descrição |
|---|---|---|
| phone_number | string | O número de telefone do usuário |
| phone_number_verified | boolean | Se o número de telefone foi verificado |
address
Consulte o OpenID Connect Core 1.0 para detalhes sobre a reivindicação de endereço.
Escopos marcados como (padrão) são sempre solicitados pelo SDK do Logto. As reivindicações sob escopos OIDC padrão são sempre incluídas no token de ID quando o escopo correspondente é solicitado — elas não podem ser desativadas.
Escopos estendidos
Os seguintes escopos são estendidos pelo Logto e retornarão reivindicações através do endpoint userinfo. Essas reivindicações também podem ser configuradas para serem incluídas diretamente no token de ID através de Console > Custom JWT. Veja Token de ID personalizado para mais detalhes.
custom_data
| Nome da reivindicação | Tipo | Descrição | Incluído no token de ID por padrão |
|---|---|---|---|
| custom_data | object | Os dados personalizados do usuário |
identities
| Nome da reivindicação | Tipo | Descrição | Incluído no token de ID por padrão |
|---|---|---|---|
| identities | object | As identidades vinculadas do usuário | |
| sso_identities | array | As identidades SSO vinculadas do usuário |
roles
| Nome da reivindicação | Tipo | Descrição | Incluído no token de ID por padrão |
|---|---|---|---|
| roles | string[] | Os papéis do usuário | ✅ |
urn:logto:scope:organizations
| Nome da reivindicação | Tipo | Descrição | Incluído no token de ID por padrão |
|---|---|---|---|
| organizations | string[] | Os IDs das organizações às quais o usuário pertence | ✅ |
| organization_data | object[] | Os dados das organizações às quais o usuário pertence |
Essas reivindicações de organização também podem ser recuperadas via endpoint userinfo ao usar um token opaco. No entanto, tokens opacos não podem ser usados como tokens de organização para acessar recursos específicos da organização. Veja Token opaco e organizações para mais detalhes.
urn:logto:scope:organization_roles
| Nome da reivindicação | Tipo | Descrição | Incluído no token de ID por padrão |
|---|---|---|---|
| organization_roles | string[] | Os papéis da organização aos quais o usuário pertence no formato <organization_id>:<role_name> | ✅ |
Recursos de API
Recomendamos ler 🔐 Controle de Acesso Baseado em Papel (RBAC) primeiro para entender os conceitos básicos do RBAC do Logto e como configurar corretamente os recursos de API.
Configurar cliente Logto
Depois de configurar os recursos de API, você pode adicioná-los ao configurar o Logto em seu aplicativo:
let config = try? LogtoConfig(
endpoint: "<your-logto-endpoint>", // Exemplo: http://localhost:3001
appId: "<your-app-id>",
resources: ["https://shopping.your-app.com/api", "https://store.your-app.com/api"], // Adicionar recursos de API
)
let client = LogtoClient(useConfig: config)
Cada recurso de API tem suas próprias permissões (escopos).
Por exemplo, o recurso https://shopping.your-app.com/api tem as permissões shopping:read e shopping:write, e o recurso https://store.your-app.com/api tem as permissões store:read e store:write.
Para solicitar essas permissões, você pode adicioná-las ao configurar o Logto em seu aplicativo:
let config = try? LogtoConfig(
endpoint: "<your-logto-endpoint>",
appId: "<your-app-id>",
scopes: ["shopping:read", "shopping:write", "store:read", "store:write"],
resources: ["https://shopping.your-app.com/api", "https://store.your-app.com/api"],
)
let client = LogtoClient(useConfig: config)
Você pode notar que os escopos são definidos separadamente dos recursos de API. Isso ocorre porque Resource Indicators for OAuth 2.0 especifica que os escopos finais para a solicitação serão o produto cartesiano de todos os escopos em todos os serviços de destino.
Assim, no caso acima, os escopos podem ser simplificados a partir da definição no Logto, ambos os recursos de API podem ter escopos read e write sem o prefixo. Então, na configuração do Logto:
let config = try? LogtoConfig(
endpoint: "<your-logto-endpoint>",
appId: "<your-app-id>",
scopes: ["read", "write"],
resources: ["https://shopping.your-app.com/api", "https://store.your-app.com/api"],
)
let client = LogtoClient(useConfig: config)
Para cada recurso de API, ele solicitará os escopos read e write.
Não há problema em solicitar escopos que não estão definidos nos recursos de API. Por exemplo, você pode solicitar o escopo email mesmo que os recursos de API não tenham o escopo email disponível. Escopos indisponíveis serão ignorados com segurança.
Após o login bem-sucedido, o Logto emitirá os escopos apropriados para os recursos de API de acordo com os papéis do usuário.
Buscar token de acesso para o recurso de API
Para buscar o token de acesso para um recurso de API específico, você pode usar o método getAccessToken:
let accessToken = try await client.getAccessToken(for: "https://shopping.your-app.com/api")
Este método retornará um token de acesso JWT que pode ser usado para acessar o recurso de API quando o usuário tiver as permissões relacionadas. Se o token de acesso em cache atual tiver expirado, este método tentará automaticamente usar um token de atualização para obter um novo token de acesso.
Anexar token de acesso aos cabeçalhos de solicitação
Coloque o token no campo Authorization dos cabeçalhos HTTP com o formato Bearer (Bearer YOUR_TOKEN), e você estará pronto para prosseguir.
O fluxo de integração do Bearer Token pode variar com base no framework ou solicitante que você está usando. Escolha sua própria maneira de aplicar o cabeçalho de solicitação Authorization.
await LogtoRequest.get(
useSession: session,
endpoint: userInfoEndpoint,
headers: ["Authorization": "Bearer \(accessToken)"]
)