busy-salesmen-35214
04/22/2025, 7:55 AMbrief-egg-61061
04/22/2025, 7:55 AMbrief-egg-61061
04/22/2025, 7:56 AMreturn_session_token_exchange_code=true
go flow, _, err := ory.FrontendApi.CreateNativeLoginFlow(ctx).Execute()
2. Render the UI with the passkey option 3. Open a browser for authentication when the user selects the passkey option - The browser is needed because WebAuthn/passkeys require browser APIs - You'll need to use a system browser, as WebView in Android doesn't support WebAuthn 4. Exchange the session token code for a session token after successful authentication [Ory Kratos Social Sign-in for Native Apps](https://www.ory.sh/docs/kratos/social-signin/native-apps#implementation) provides a detailed implementation guide that follows a similar pattern. ## Platform Support It's important to note that WebAuthn/passkey support varies by platform: - iOS supports passkeys through Apple Passkeys - Android has added passkey support in recent versions - Cross-platform passkeys allow authentication across different devices and platforms [Login and authentication explained](https://www.ory.sh/blog/overview-login-password-passkey-webauthn-totp-sso-faceid) ## Current Limitations There are some limitations to be aware of: - As noted in a GitHub issue, "We do not support WebAuthN on native devices for now. The current implementation of it is pretty browser focused and requires JS and browser APIs." [GitHub Issue #284](https://github.com/ory/network/issues/284) - For native apps, you'll need to use a browser to complete the WebAuthn/passkey authentication ## Configuration To enable passkeys in your Ory project: 1. Go to Authentication → Passwordless login in the Ory Console 2. Toggle the switch for Enable Passkey authentication Alternatively, use the Ory CLI: ory patch identity-config <your-project-id> \ --add '/selfservice/methods/passkey/enabled=true' \ --add '/selfservice/methods/passkey/config/rp/display_name="My Display Name"'
For the most up-to-date implementation details, I recommend checking the [Ory documentation on passkeys](https://www.ory.sh/docs/kratos/passwordless/passkeys) and the [native app authentication flow](https://www.ory.sh/docs/kratos/social-signin/native-apps#implementation).busy-salesmen-35214
04/22/2025, 11:01 AMbrief-egg-61061
04/22/2025, 11:01 AMbrief-egg-61061
04/22/2025, 11:02 AMjavascript const express = require("express") const cors = require("cors") const { FrontendApi, Configuration } = require("@ory/client-fetch") const app = express() const ory = new FrontendApi( new Configuration({ basePath: process.env.ORY_URL || "<http://localhost:4000>", baseOptions: { withCredentials: true }, }), ) app.use( cors({ origin: process.env.UI_URL || "<http://localhost:8080>", credentials: true, // Required for CORS to accept cookies and tokens. }), ) app.use((req, res, next) => { // Forward the cookies from the request to Ory ory .toSession({ cookie: req.headers.cookie, }) .then(({ data }) => { req.session = data next() }) .catch(() => { res.status(401) res.json({ error: "Unauthorized" }) }) })
[Integrate authentication into Vue.js + API](https://www.ory.sh/docs/getting-started/integrate-auth/vue#make-authenticated-calls-to-your-api) ### In Single Page Applications (SPA) For SPAs making AJAX requests, you need to set withCredentials: true
or `credentials: "include"`: javascript const { FrontendApi, Configuration } = require("@ory/client-fetch") const ory = new FrontendApi( new Configuration({ basePath: `{YOUR-CUSTOM-DOMAIN-OR-ORY-TUNNEL}`, baseOptions: { withCredentials: true, }, }), ) const session = await ory.toSession().catch((err) => { // Check the error to see if it's a 401 / 403 -> not logged in })
[Check session status](https://www.ory.sh/docs/identities/sign-in/check-session-token-cookie-api) ## Native Applications For native apps, you'll use session tokens instead of cookies: javascript const { FrontendApi, Configuration } = require("@ory/client") const frontend = new FrontendApi( new Configuration({ basePath: `https://${process.env.PROJECT_SLUG}.<http://projects.oryapis.com|projects.oryapis.com>`, }), ) export async function checkSession(sessionId: string, token: string) { return await frontend.toSession({ xSessionToken: token, }) }
[Session Checks](https://www.ory.sh/docs/kratos/bring-your-own-ui/custom-ui-basic-integration#session-checks) ## Important Notes 1. When working with multiple cookies, ensure they're properly formatted with semicolons: ClientApi.Configuration.DefaultHeaders.Add("Cookie", csrfCookie + "; " + sessionCookie);
[GitHub Issue #36](https://github.com/ory/network/issues/36) 2. For backend services that need to handle both browser and native app requests: javascript app.get("/blog/posts", async function (req, res) { const authHeader = req.headers.authorization const hasAuthHeader = authHeader.startsWith("Bearer ") const sessionToken = hasAuthHeader ? authHeader.slice(7, authHeader.length) : null const session = await ory .toSession({ cookie: req.cookies.join("; "), xSessionToken: sessionToken, }) .catch((err) => { // Handle error }) })
[Check session status](https://www.ory.sh/docs/identities/sign-in/check-session-token-cookie-api) 3. For cURL requests, use the -b
flag to read cookies from a file: curl -X GET \ -H 'Content-Type: application/json' \ -H 'Accept: application/json' \ -b cookies.txt \ https://$<http://PROJECT_SLUG.projects.oryapis.com/session/whoami|PROJECT_SLUG.projects.oryapis.com/session/whoami>
[Session Checks](https://www.ory.sh/docs/kratos/bring-your-own-ui/custom-ui-basic-integration#session-checks) Remember that for security reasons, Ory uses different endpoints for browser and native applications, so make sure you're using the correct one for your use case.busy-salesmen-35214
04/22/2025, 11:06 AMcsrf_token
only for browser-based flows, and session cookies for authenticated API flows.
how to use in the retrofit native mobile apps @chilly-king-10285brief-egg-61061
04/22/2025, 11:06 AMbrief-egg-61061
04/22/2025, 11:06 AM/self-service/{flow_type}/api
endpoints instead of the browser endpoints: kotlin // Retrofit service interface interface OryService { @GET("/self-service/login/api") suspend fun createLoginFlow(): Response<LoginFlow> @GET("/self-service/registration/api") suspend fun createRegistrationFlow(): Response<RegistrationFlow> // Other flow endpoints... }
[Native vs. browser apps](https://www.ory.sh/docs/identities/native-browser) ### 2. Handle Session Tokens (Not Cookies) Native apps receive session tokens instead of cookies: kotlin // Add an interceptor to your Retrofit client to handle session tokens val client = OkHttpClient.Builder() .addInterceptor { chain -> val original = chain.request() val request = original.newBuilder() // Add session token if available sessionToken?.let { request.header("Authorization", "Bearer $it") } chain.proceed(request.build()) } .build() val retrofit = Retrofit.Builder() .baseUrl("https://$<http://PROJECT_SLUG.projects.oryapis.com|PROJECT_SLUG.projects.oryapis.com>") .client(client) .addConverterFactory(GsonConverterFactory.create()) .build()
[Using Ory Session Token](https://www.ory.sh/docs/kratos/session-management/overview#using-ory-session-token) ### 3. Check Session Status To verify if a user is authenticated: kotlin interface OryService { @GET("/sessions/whoami") suspend fun checkSession(@Header("Authorization") sessionToken: String): Response<Session> } // Usage try { val response = oryService.checkSession("Bearer $sessionToken") if (response.isSuccessful) { // User is authenticated val session = response.body() // Use session data } } catch (e: Exception) { // Handle error - user not authenticated }
[Check session status](https://www.ory.sh/docs/identities/sign-in/check-session-token-cookie-api) ### 4. Social Sign-in or Passkeys in Native Apps For authentication methods that require browser capabilities (like social sign-in or passkeys): 1. Create an API flow with return_session_token_exchange_code=true
2. When the user selects a method requiring a browser, open a system browser 3. After authentication, the browser will redirect back to your app with a code 4. Exchange this code for a session token kotlin // Step 1: Create flow val flow = oryService.createLoginFlow(returnSessionTokenExchangeCode = true) // Step 2: Open browser when needed val redirectUrl = "<your-app-scheme://callback>" val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(flow.redirectBrowserTo)) startActivity(browserIntent) // Step 3 & 4: Handle callback and exchange code // In your callback activity fun handleCallback(uri: Uri) { val code = uri.getQueryParameter("code") code?.let { // Exchange code for session token val sessionToken = oryService.exchangeSessionToken(code) // Store session token securely secureStorage.saveSessionToken(sessionToken) } }
[Social sign-in for native apps](https://www.ory.sh/docs/kratos/social-signin/native-apps#implementation) ### Important Considerations 1. No CSRF Tokens: Native apps don't need CSRF protection, so you don't need to handle CSRF tokens. 2. Secure Storage: Store the session token securely using platform-specific secure storage (KeyStore for Android, Keychain for iOS). 3. Token Refresh: Implement token refresh logic when the session expires. 4. Browser Authentication: For methods requiring browser capabilities, you'll need to implement deep linking to handle the redirect back to your app. By following these guidelines, you can properly integrate Ory Kratos authentication in your Retrofit-based native mobile application without dealing with browser-specific concerns like CSRF tokens. Remember that for native apps, you should always use the /api
endpoints rather than the /browser
endpoints to ensure proper security measures are applied.busy-salesmen-35214
04/22/2025, 11:42 AMbusy-salesmen-35214
04/22/2025, 11:43 AMbrief-egg-61061
04/22/2025, 11:43 AMbrief-egg-61061
04/22/2025, 11:43 AMtoSession
method with the session token: go package frontend import ( "context" "fmt" "os" "<http://github.com/ory/client-go|github.com/ory/client-go>" ) type oryMiddleware struct { ory *ory.APIClient } func init() { cfg := client.NewConfiguration() cfg.Servers = client.ServerConfigurations{ {URL: fmt.Sprintf("<https://%s.projects.oryapis.com>", os.Getenv("ORY_PROJECT_SLUG"))}, } ory = client.NewAPIClient(cfg) } func CheckSession(ctx context.Context, sessionToken string) (session *client.Session, err error) { session, _, err = ory.FrontendApi.ToSession(ctx). XSessionToken(sessionToken). Execute() if err != nil { // error revoking the session, for example due to expired token provided return nil, err } return session, nil }
[Ory Identity Management Go SDK](https://www.ory.sh/docs/kratos/sdk/go#native---check-who-the-current-http-session-belongs-to) ### 2. Including the Token in API Requests You can include the session token in API requests in two ways: 1. As an X-Session-Token
header 2. As a Bearer token in the Authorization
header // Using X-Session-Token header curl -X GET \ -H 'Content-Type: application/json' \ -H 'Accept: application/json' \ -H 'X-Session-Token: <your-session-token>' \ https://$<http://PROJECT_SLUG.projects.oryapis.com/sessions/whoami|PROJECT_SLUG.projects.oryapis.com/sessions/whoami> // Using Authorization header curl -X GET \ -H 'Content-Type: application/json' \ -H 'Accept: application/json' \ -H 'Authorization: Bearer <your-session-token>' \ https://$<http://PROJECT_SLUG.projects.oryapis.com/sessions/whoami|PROJECT_SLUG.projects.oryapis.com/sessions/whoami>
[Session Checks](https://www.ory.sh/docs/kratos/bring-your-own-ui/custom-ui-basic-integration#session-checks) ### 3. Storing the Session Token In React Native, you can store the session token securely: javascript import { Configuration, FrontendApi, Session } from "@ory/client" import { useEffect, useState } from "react" import * as SecureStore from "expo-secure-store" const ory = new FrontendApi( new Configuration({ basePath: `https://$<http://PROJECT_SLUG.projects.oryapis.com|PROJECT_SLUG.projects.oryapis.com>`, }), ) async function getSessionTokenFromStore() { return await SecureStore.getItemAsync("session_token") } export default async function SomeComponent() { const [session, setSession] = useState<Session | undefined>() const sessionToken = await getSessionTokenFromStore() useEffect(() => { if (!sessionToken) { return } ory .toSession({ xSessionToken: sessionToken, }) .then(({ data }) => setSession(data)) }, [sessionToken]) return <div>{session?.id}</div> }
[Check session status](https://www.ory.sh/docs/identities/sign-in/check-session-token-cookie-api) ### 4. Logging Out (Revoking the Session) To log out a user by revoking their session token: go func Logout(ctx context.Context, sessionToken string) error { _, err := ory.FrontendApi.PerformNativeLogout(ctx). PerformNativeLogoutBody(*client.NewPerformNativeLogoutBody(sessionToken)). Execute() if err != nil { return err } // Logout was successful return nil }
[Native API applications](https://www.ory.sh/docs/kratos/self-service/flows/user-logout#native-api-applications) Remember that session tokens are meant for native applications, while cookies are for browser applications. For security reasons, you can't break the isolation between these two authentication methods.busy-salesmen-35214
04/22/2025, 11:45 AMbrief-egg-61061
04/22/2025, 11:45 AMbrief-egg-61061
04/22/2025, 11:45 AM/api
endpoints (not the /browser
endpoints), and these don't require CSRF tokens. Instead, they use session tokens for authentication. Let me provide a complete example of how to implement authentication in a native app using Retrofit (Android): kotlin import retrofit2.Call import retrofit2.Retrofit import retrofit2.converter.gson.GsonConverterFactory import retrofit2.http.GET import <http://retrofit2.http.POST|retrofit2.http.POST> import retrofit2.http.Body import retrofit2.http.Header import okhttp3.OkHttpClient // Data classes for requests/responses data class LoginFlow( val id: String, val type: String, val ui: UiContainer ) data class UiContainer( val action: String, val method: String, val nodes: List<UiNode> ) data class UiNode( val type: String, val attributes: Map<String, Any> ) data class LoginRequest( val identifier: String, val password: String, val method: String = "password" ) data class Session( val id: String, val identity: Identity ) data class Identity( val id: String, val traits: Map<String, Any> ) // Retrofit service interface interface OryService { @GET("/self-service/login/api") fun createLoginFlow(): Call<LoginFlow> @POST("/self-service/login") fun submitLogin( @Body request: LoginRequest, @Header("X-Session-Token") sessionToken: String? = null ): Call<Session> @GET("/sessions/whoami") fun checkSession( @Header("X-Session-Token") sessionToken: String ): Call<Session> } // Implementation class OryClient(private val projectSlug: String) { private val client = OkHttpClient.Builder().build() private val retrofit = Retrofit.Builder() .baseUrl("https://$<http://projectSlug.projects.oryapis.com/%22|projectSlug.projects.oryapis.com/">) .client(client) .addConverterFactory(GsonConverterFactory.create()) .build() private val service = retrofit.create(OryService::class.java) // Initialize login flow fun createLoginFlow(): LoginFlow? { val call = service.createLoginFlow() val response = call.execute() if (response.isSuccessful) { return response.body() } return null } // Submit login credentials fun login(flowId: String, username: String, password: String): Session? { val loginRequest = LoginRequest( identifier = username, password = password ) val call = service.submitLogin(loginRequest) val response = call.execute() if (response.isSuccessful) { // Store the session token securely val session = response.body() // session?.token contains the session token to store return session } return null } // Check if session is valid fun checkSession(sessionToken: String): Session? { val call = service.checkSession(sessionToken) val response = call.execute() if (response.isSuccessful) { return response.body() } return null } } // Usage example fun authenticateUser(username: String, password: String) { val oryClient = OryClient("your-project-slug") // Step 1: Create login flow val flow = oryClient.createLoginFlow() // Step 2: Submit credentials if (flow != null) { val session = oryClient.login(flow.id, username, password) // Step 3: Store session token securely if (session != null) { // Store session.id or the session token in secure storage // For Android: EncryptedSharedPreferences or Keystore // For iOS: Keychain } } }
Remember these key points: 1. Native apps should use the /api
endpoints, not the /browser
endpoints 2. CSRF tokens are only used for browser-based flows, not for native apps [Native vs. browser apps](https://www.ory.sh/docs/identities/native-browser) 3. Native apps receive session tokens instead of cookies 4. Store the session token securely using platform-specific secure storage If you're implementing authentication methods that require browser capabilities (like social sign-in or passkeys), you'll need to use a system browser and handle the redirect back to your app, but that's a different flow than what you're asking about.busy-salesmen-35214
04/22/2025, 12:59 PMbrief-egg-61061
04/22/2025, 12:59 PMbrief-egg-61061
04/22/2025, 1:00 PM/self-service/registration/api
endpoint (which you are), but there's a key misunderstanding about CSRF tokens in this context. ## CSRF Tokens in Native Apps When using the /api
endpoints (as opposed to /browser
endpoints), you don't need to handle CSRF tokens at all. As the documentation states: > The application that consumes Ory APIs through its UI directly determines which API endpoints must be called and, as a result, what security measures are applied. > > Native applications, such as Android mobile apps or desktop applications, aren't rendered in the browser. Since the application isn't rendered in the browser, the CSRF cookie and token aren't necessary. [Integration basics](https://www.ory.sh/docs/kratos/bring-your-own-ui/custom-ui-basic-integration) ## Correct Implementation for Native Apps For your Android app using Retrofit, you should: 1. Call the /self-service/registration/api
endpoint to create a registration flow 2. Extract the flow ID from the response 3. Submit the registration data to /self-service/registration?flow=<your-flow-id>
without worrying about the CSRF token Here's a sample implementation: kotlin // Create the registration flow val flowResponse = oryService.createRegistrationFlow().execute() val flow = flowResponse.body() val flowId = flow?.id // Submit registration data val registrationRequest = RegistrationRequest( method = "password", password = "your-password", traits = mapOf("email" to "<mailto:user@example.com|user@example.com>") // Add other required traits based on your identity schema ) // No need to include CSRF token for native apps val response = oryService.submitRegistration(flowId, registrationRequest).execute() // Handle the response if (response.isSuccessful) { // Registration successful, handle session val session = response.body() // Store session token securely }
If you're seeing a CSRF token in the response, it's because the API returns the same flow structure for both browser and native apps, but for native apps, you can safely ignore the CSRF token field. If you're still experiencing issues, make sure your Retrofit service is correctly configured to use the native app endpoints and not the browser endpoints. Let me know if you need more specific help with your implementation!busy-salesmen-35214
04/24/2025, 10:32 AMbrief-egg-61061
04/24/2025, 10:32 AMbrief-egg-61061
04/24/2025, 10:32 AMreturn_session_token_exchange_code=true
2. When the user selects passkey authentication, open a system browser (not WebView) 3. After successful authentication in the browser, the user will be redirected back to your app 4. Exchange the session token code for a session token This approach is similar to how social sign-in works in native apps, as described in the [Social sign-in for native apps](https://www.ory.sh/docs/kratos/social-signin/native-apps#implementation) documentation. The key limitation is that the actual passkey authentication must happen in a browser context, as WebAuthn requires browser APIs that aren't available in native app contexts. If passkey authentication is critical for your native app, you might want to consider platform-specific biometric authentication methods as an alternative until Ory adds direct native support for passkeys.busy-salesmen-35214
04/25/2025, 9:16 AMval options = root1
.getJSONObject("methods")
.getJSONObject("webauthn")
.getJSONObject("config")
.getJSONObject("publicKeyCredentialRequestOptions")
how we can get the data above in android from login api ? @chilly-king-10285brief-egg-61061
04/25/2025, 9:16 AMbrief-egg-61061
04/25/2025, 9:17 AMkotlin // Define data classes for the response structure data class LoginFlow( val id: String, val type: String, val ui: UiContainer, val methods: Map<String, MethodConfig> ) data class MethodConfig( val config: WebAuthnConfig? ) data class WebAuthnConfig( val publicKeyCredentialRequestOptions: PublicKeyCredentialRequestOptions ) data class PublicKeyCredentialRequestOptions( val challenge: String, val timeout: Long, val rpId: String, val allowCredentials: List<AllowCredential>, val userVerification: String ) data class AllowCredential( val type: String, val id: String ) // Retrofit service interface interface OryService { @GET("/self-service/login/api") suspend fun createLoginFlow(): Response<LoginFlow> } // Usage in your activity/fragment suspend fun initiateWebAuthnLogin() { try { val response = oryService.createLoginFlow() if (response.isSuccessful) { val loginFlow = response.body() // Extract WebAuthn options val webAuthnConfig = loginFlow?.methods?.get("webauthn")?.config val publicKeyOptions = webAuthnConfig?.publicKeyCredentialRequestOptions if (publicKeyOptions != null) { // Now you have the data to trigger WebAuthn authentication // This is similar to what you were extracting in your JSON snippet val challenge = publicKeyOptions.challenge val rpId = publicKeyOptions.rpId val allowCredentials = publicKeyOptions.allowCredentials // Use these values with the WebAuthn API triggerWebAuthnAuthentication(publicKeyOptions) } } } catch (e: Exception) { // Handle error } }
As mentioned in our previous conversation, WebAuthn is primarily designed for browsers. For native Android apps, you'll need to: 1. Open a system browser for the actual WebAuthn authentication 2. Handle the redirect back to your app 3. Exchange the authentication result for a session token The JSON structure you're trying to parse contains the WebAuthn challenge data that would typically be passed to the browser's navigator.credentials.get()
function. In a native app context, you'll need to pass this data to the browser component that handles the WebAuthn authentication. For a complete implementation, you should follow the pattern described in the [passwordless authentication documentation](https://www.ory.sh/docs/kratos/bring-your-own-ui/custom-ui-advanced-integration#passwordless-authentication), adapting it for the native app context.busy-salesmen-35214
04/25/2025, 9:18 AMbrief-egg-61061
04/25/2025, 9:18 AMbrief-egg-61061
04/25/2025, 9:19 AMkotlin import android.content.Intent import android.net.Uri import android.os.Bundle import android.util.Base64 import androidx.appcompat.app.AppCompatActivity import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import okhttp3.OkHttpClient import retrofit2.Response import retrofit2.Retrofit import retrofit2.converter.gson.GsonConverterFactory import retrofit2.http.GET import retrofit2.http.Query // Data classes for Ory API responses data class LoginFlow( val id: String, val methods: Methods ) data class Methods( val webauthn: WebAuthnMethod ) data class WebAuthnMethod( val config: WebAuthnConfig ) data class WebAuthnConfig( val publicKeyCredentialRequestOptions: PublicKeyCredentialRequestOptions ) data class PublicKeyCredentialRequestOptions( val challenge: String, val rpId: String, val timeout: Long, val allowCredentials: List<AllowCredential>, val userVerification: String ) data class AllowCredential( val id: String, val type: String ) // Retrofit service interface interface OryService { @GET("/self-service/login/api") suspend fun createLoginFlow( @Query("return_session_token_exchange_code") returnCode: Boolean = true ): Response<LoginFlow> } class WebAuthnLoginActivity : AppCompatActivity() { private lateinit var oryService: OryService private val projectSlug = "your-project-slug" override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_webauthn_login) // Initialize Retrofit val retrofit = Retrofit.Builder() .baseUrl("https://$<http://projectSlug.projects.oryapis.com|projectSlug.projects.oryapis.com>") .addConverterFactory(GsonConverterFactory.create()) .client(OkHttpClient.Builder().build()) .build() oryService = retrofit.create(OryService::class.java) // Start the WebAuthn login process initiateWebAuthnLogin() } private fun initiateWebAuthnLogin() { CoroutineScope(<http://Dispatchers.IO|Dispatchers.IO>).launch { try { val response = oryService.createLoginFlow(true) if (response.isSuccessful) { val loginFlow = response.body() loginFlow?.let { // Extract WebAuthn options val webAuthnConfig = it.methods.webauthn.config val publicKeyOptions = webAuthnConfig.publicKeyCredentialRequestOptions // Create a URL to open in the browser val browserUrl = createWebAuthnBrowserUrl(it.id, publicKeyOptions) // Switch to main thread to open browser CoroutineScope(Dispatchers.Main).launch { openBrowserForWebAuthn(browserUrl) } } } else { // Handle error } } catch (e: Exception) { // Handle exception } } } private fun createWebAuthnBrowserUrl(flowId: String, options: PublicKeyCredentialRequestOptions): String { // Encode the challenge for URL safety val encodedChallenge = Base64.encodeToString( Base64.decode(options.challenge, Base64.DEFAULT), Base64.URL_SAFE ) // Create a deep link URL that will redirect back to your app val redirectUri = "<yourapp://auth/callback>" // Construct the URL to the Ory hosted UI or your custom UI that handles WebAuthn return "https://$<http://projectSlug.projects.oryapis.com/ui/login|projectSlug.projects.oryapis.com/ui/login>" + "?flow=$flowId" + "&redirect_uri=$redirectUri" + "&auth_method=webauthn" } private fun openBrowserForWebAuthn(url: String) { // Open the system browser with the WebAuthn URL val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url)) startActivity(intent) } // Handle the callback from the browser override fun onNewIntent(intent: Intent) { super.onNewIntent(intent) val uri = intent.data if (uri != null && uri.toString().startsWith("<yourapp://auth/callback>")) { // Extract the session token exchange code val code = uri.getQueryParameter("code") if (code != null) { // Exchange the code for a session token exchangeCodeForSessionToken(code) } } } private fun exchangeCodeForSessionToken(code: String) { // Implement the code exchange API call // This would be another Retrofit call to exchange the code for a session token // Store the session token securely for future authenticated requests } }
To make this work, you'll need to: 1. Add the necessary dependencies to your `build.gradle`: gradle implementation 'com.squareup.retrofit2:retrofit:2.9.0' implementation 'com.squareup.retrofit2:converter-gson:2.9.0' implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4'
2. Configure deep linking in your `AndroidManifest.xml`: xml <activity android:name=".WebAuthnLoginActivity"> <intent-filter> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE" /> <data android:scheme="yourapp" android:host="auth" android:pathPrefix="/callback" /> </intent-filter> </activity>
Remember that this implementation uses a system browser for the WebAuthn authentication, as WebAuthn is not directly supported in native Android apps. The browser will redirect back to your app after authentication is complete. This approach follows the pattern described in the [Ory documentation for native apps](https://www.ory.sh/docs/kratos/social-signin/native-apps#implementation), adapted for WebAuthn/passkey authentication.busy-salesmen-35214
04/25/2025, 9:20 AMbrief-egg-61061
04/25/2025, 9:20 AMbrief-egg-61061
04/25/2025, 9:20 AMWebAuthnLoginActivity.kt
. For a complete implementation, you would need to: 1. Create a new Android project in Android Studio 2. Add the necessary dependencies to your build.gradle
file 3. Create the layout file activity_webauthn_login.xml
4. Add the deep linking configuration to your AndroidManifest.xml
5. Copy the Kotlin code I provided into WebAuthnLoginActivity.kt
You can also find reference implementations in Ory's GitHub repositories: - [Ory Kratos Selfservice UI Node](https://github.com/ory/kratos-selfservice-ui-node) - [Ory Kratos Selfservice UI React NextJS](https://github.com/ory/kratos-selfservice-ui-react-nextjs) These repositories contain complete implementations that you can adapt for your Android application. If you need more specific guidance on implementing certain parts of the code, please let me know and I'd be happy to provide more detailed explanations.busy-salesmen-35214
04/25/2025, 9:40 AMbrief-egg-61061
04/25/2025, 9:40 AMbrief-egg-61061
04/25/2025, 9:41 AMmethods
object or the webauthn
property within it is null. This typically happens for one of these reasons: 1. WebAuthn might not be enabled in your Ory project configuration 2. The JSON response structure doesn't match your data classes 3. The login flow response doesn't include WebAuthn as an available method Here's how to fix this issue: kotlin // Update your code to handle potential null values private fun initiateWebAuthnLogin() { CoroutineScope(<http://Dispatchers.IO|Dispatchers.IO>).launch { try { val response = oryService.createLoginFlow(true) if (response.isSuccessful) { val loginFlow = response.body() loginFlow?.let { // Check if methods and webauthn are not null if (it.methods?.webauthn != null) { val webAuthnConfig = it.methods.webauthn.config val publicKeyOptions = webAuthnConfig.publicKeyCredentialRequestOptions // Create a URL to open in the browser val browserUrl = createWebAuthnBrowserUrl(it.id, publicKeyOptions) // Switch to main thread to open browser CoroutineScope(Dispatchers.Main).launch { openBrowserForWebAuthn(browserUrl) } } else { // Handle case where WebAuthn is not available Log.e("WebAuthnLogin", "WebAuthn method is not available in this flow") } } } else { // Handle error } } catch (e: Exception) { // Handle exception Log.e("WebAuthnLogin", "Error: ${e.message}", e) } } }
Also, update your data classes to make the properties nullable: kotlin data class Methods( val webauthn: WebAuthnMethod? = null )
To debug this issue: 1. First, check if WebAuthn is enabled in your Ory project settings 2. Log the raw JSON response to see its actual structure: kotlin val responseBody = response.body() Log.d("OryResponse", "Raw response: ${Gson().toJson(responseBody)}")
3. Make sure your data classes match the actual JSON structure from the API Remember that WebAuthn might not be available as a method if: - It's not enabled in your Ory configuration - The user doesn't have any WebAuthn credentials registered - You're using an API endpoint that doesn't support WebAuthn If WebAuthn is definitely enabled in your project, you might need to adjust your data classes to match the exact structure of the Ory API response.busy-salesmen-35214
04/25/2025, 9:43 AMbusy-salesmen-35214
04/25/2025, 9:44 AMbrief-egg-61061
04/25/2025, 9:44 AMbrief-egg-61061
04/25/2025, 9:44 AM/self-service/login/api
- This endpoint creates a login flow that can include WebAuthn as an authentication method if it's enabled in your Ory configuration. 2. For WebAuthn JavaScript, Ory provides a specific endpoint: GET /.well-known/ory/webauthn.js
This endpoint provides the JavaScript needed to perform WebAuthn login and registration [Ory API Reference](https://www.ory.sh/docs/reference/api). However, there's an important limitation to be aware of: > "WebAuthn is a browser standard. It does not work in native mobile apps." [Passkeys & WebAuthN - Constraints](https://www.ory.sh/docs/kratos/passwordless/passkeys#constraints) This is further confirmed by an Ory team member: > "We do not support WebAuthN on native devices for now. The current implementation of it is pretty browser focused and requires JS and browser APIs." [GitHub Issue #284](https://github.com/ory/network/issues/284) For native apps, the recommended approach is to: 1. Create a login flow with the API endpoint 2. Open a system browser (not WebView) for the actual WebAuthn authentication 3. Handle the redirect back to your app after authentication Make sure WebAuthn is properly enabled in your Ory configuration: selfservice: methods: webauthn: enabled: true config: passwordless: false rp: id: <http://example.org|example.org> origins: - <http://auth.example.org:4455> display_name: Ory
If you're still having issues, you might want to check if your Ory project has WebAuthn properly configured and enabled.busy-salesmen-35214
04/25/2025, 9:47 AMbrief-egg-61061
04/25/2025, 9:47 AMbrief-egg-61061
04/25/2025, 9:47 AMkotlin import android.content.Intent import android.net.Uri import androidx.appcompat.app.AppCompatActivity class WebAuthnActivity : AppCompatActivity() { private fun openSystemBrowserForWebAuthn(url: String) { // Create an intent with ACTION_VIEW val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url)) // Ensure we're using the system browser, not an in-app WebView intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) // Optional: Force the use of the system browser intent.setPackage("com.android.chrome") // For Chrome // If Chrome isn't available, fall back to the default browser if (intent.resolveActivity(packageManager) == null) { intent.setPackage(null) } // Start the activity startActivity(intent) } }
## Using Custom Tabs (Recommended Approach) For a better user experience, you can use Chrome Custom Tabs which provide a more integrated experience while still using the system browser capabilities: kotlin import androidx.browser.customtabs.CustomTabsIntent import android.net.Uri fun openCustomTabForWebAuthn(url: String) { val customTabsIntent = CustomTabsIntent.Builder() .setShowTitle(true) .build() customTabsIntent.launchUrl(context, Uri.parse(url)) }
For this approach, add the dependency to your `build.gradle`: gradle implementation 'androidx.browser:browser:1.4.0'
## Handling the Redirect Back to Your App To handle the redirect back to your app after authentication: 1. Define a custom URL scheme in your `AndroidManifest.xml`: xml <activity android:name=".CallbackActivity"> <intent-filter> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE" /> <data android:scheme="yourapp" android:host="callback" /> </intent-filter> </activity>
2. Create the callback URL to include in your authentication request: kotlin val callbackUrl = "<yourapp://callback>" val authUrl = "https://$<http://PROJECT_SLUG.projects.oryapis.com/ui/login?flow=$flowId&return_to=$callbackUrl|PROJECT_SLUG.projects.oryapis.com/ui/login?flow=$flowId&return_to=$callbackUrl>"
3. Handle the incoming intent in your `CallbackActivity`: kotlin override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val uri = intent.data if (uri != null && uri.scheme == "yourapp") { // Extract parameters from the URI val code = uri.getQueryParameter("code") // Exchange the code for a session token if (code != null) { exchangeCodeForSessionToken(code) } } }
This approach follows the pattern described in the [Social sign-in for native apps](https://www.ory.sh/docs/kratos/social-signin/native-apps#implementation) documentation, which uses a similar browser-based authentication flow. Remember that WebAuthn requires browser capabilities and cannot be implemented directly in native apps without using a browser component.busy-salesmen-35214
04/25/2025, 10:02 AMbrief-egg-61061
04/25/2025, 10:02 AMbrief-egg-61061
04/25/2025, 10:03 AMjson { "error": { "debug": "The database adapter was unable to find the element", "error": "The requested resource could not be found", "error_description": "Object with ID 12345 does not exist", "message": "The requested resource could not be found", "status_code": 404 } }
Common causes for 404 errors in Ory include: 1. Incorrect project slug or domain in your API URL 2. Trying to access a flow that has expired or doesn't exist 3. Attempting to retrieve a non-existent identity, session, or project 4. Using an incorrect or outdated API endpoint 5. Typos in your URL path To troubleshoot this issue: 1. Double-check your API URL and project slug 2. Verify that the resource ID you're trying to access exists 3. Ensure you're using the correct API version 4. Check if the flow you're trying to access hasn't expired (flows like login, registration, etc. have expiration times) 5. Confirm you're using the correct endpoint for your application type (browser vs. API) If you can provide more specific details about which endpoint you're calling and the complete error response, I can give you more targeted assistance.busy-salesmen-35214
04/25/2025, 12:38 PMbrief-egg-61061
04/25/2025, 12:38 PMbrief-egg-61061
04/25/2025, 12:38 PMkotlin interface OryService { @GET("/self-service/login/api") suspend fun createLoginFlow( @Query("return_session_token_exchange_code") returnCode: Boolean = true ): Response<LoginFlow> } // Usage val response = oryService.createLoginFlow(true) val loginFlow = response.body()
2. Extract the social sign-in provider information: kotlin // Find the social sign-in provider (e.g., "google", "github") val providers = loginFlow.ui.nodes .filter { it.group == "oidc" } .map { it.attributes["value"].toString() }
3. Open a system browser for the authentication (not WebView): kotlin // Create the URL for the selected provider val providerId = "google" // Or whichever provider the user selected val browserUrl = "https://$<http://PROJECT_SLUG.projects.oryapis.com/self-service/methods/oidc/auth/$providerId?flow=${loginFlow.id}|PROJECT_SLUG.projects.oryapis.com/self-service/methods/oidc/auth/$providerId?flow=${loginFlow.id}>" // Open system browser val intent = Intent(Intent.ACTION_VIEW, Uri.parse(browserUrl)) startActivity(intent)
4. Configure deep linking to handle the redirect back to your app: In your `AndroidManifest.xml`: xml <activity android:name=".CallbackActivity"> <intent-filter> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE" /> <data android:scheme="yourapp" android:host="callback" /> </intent-filter> </activity>
5. Exchange the code for a session token in your callback activity: kotlin override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val uri = intent.data if (uri != null && uri.scheme == "yourapp") { val code = uri.getQueryParameter("code") if (code != null) { // Exchange code for session token exchangeCodeForSessionToken(code) } } } private fun exchangeCodeForSessionToken(code: String) { CoroutineScope(<http://Dispatchers.IO|Dispatchers.IO>).launch { try { val response = oryService.exchangeSessionToken(code) if (response.isSuccessful) { val sessionToken = response.body()?.session_token // Store session token securely secureStorage.saveSessionToken(sessionToken) // Navigate to main screen startActivity(Intent(this@CallbackActivity, MainActivity::class.java)) finish() } } catch (e: Exception) { // Handle error } } }
This approach follows the pattern described in the [Social sign-in for native apps](https://www.ory.sh/docs/kratos/social-signin/native-apps#implementation) documentation. Remember that for native apps, you don't directly implement the callback URL you mentioned. Instead, you configure a deep link in your app and use the system browser for the authentication flow, which will then redirect back to your app.busy-salesmen-35214
04/28/2025, 6:10 AMbrief-egg-61061
04/28/2025, 6:10 AMbrief-egg-61061
04/28/2025, 6:11 AMkotlin interface OryService { @GET("/self-service/registration/api") suspend fun createRegistrationFlow( @Query("return_session_token_exchange_code") returnCode: Boolean = true ): Response<RegistrationFlow> } // Usage val response = oryService.createRegistrationFlow(true) val registrationFlow = response.body()
### 2. Open a System Browser for Passkey Creation Since WebAuthn requires browser APIs, you need to open a system browser: kotlin // Create the URL for passkey registration val flowId = registrationFlow.id val callbackUrl = "<yourapp://callback>" val browserUrl = "https://$<http://PROJECT_SLUG.projects.oryapis.com/ui/registration?flow=$flowId&return_to=$callbackUrl|PROJECT_SLUG.projects.oryapis.com/ui/registration?flow=$flowId&return_to=$callbackUrl>" // Open system browser (not WebView) val intent = Intent(Intent.ACTION_VIEW, Uri.parse(browserUrl)) startActivity(intent) // Alternatively, use Chrome Custom Tabs for better UX val customTabsIntent = CustomTabsIntent.Builder().build() customTabsIntent.launchUrl(context, Uri.parse(browserUrl))
### 3. Configure Deep Linking In your `AndroidManifest.xml`: xml <activity android:name=".CallbackActivity"> <intent-filter> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE" /> <data android:scheme="yourapp" android:host="callback" /> </intent-filter> </activity>
### 4. Handle the Callback kotlin override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val uri = intent.data if (uri != null && uri.scheme == "yourapp") { val code = uri.getQueryParameter("code") if (code != null) { // Exchange code for session token exchangeCodeForSessionToken(code) } } } private fun exchangeCodeForSessionToken(code: String) { CoroutineScope(<http://Dispatchers.IO|Dispatchers.IO>).launch { try { val response = oryService.exchangeSessionToken(code) if (response.isSuccessful) { val sessionToken = response.body()?.session_token // Store session token securely secureStorage.saveSessionToken(sessionToken) // Navigate to main screen startActivity(Intent(this@CallbackActivity, MainActivity::class.java)) finish() } } catch (e: Exception) { // Handle error } } }
## Configuration in Ory Before implementing this, ensure passkeys are enabled in your Ory project: 1. Go to Authentication → Passwordless login in the [Ory Console](https://console.ory.sh/projects/current/passwordless) 2. Toggle the switch for Enable Passkey authentication Alternatively, use the Ory CLI: ory patch identity-config <your-project-id> \ --add '/selfservice/methods/passkey/enabled=true' \ --add '/selfservice/methods/passkey/config/rp/display_name="My Display Name"'
## Summary The implementation for passkey creation follows the same pattern as social sign-in in native apps: 1. Create a flow with return_session_token_exchange_code=true
2. Open a system browser for the actual WebAuthn/passkey creation 3. Handle the redirect back to your app via deep linking 4. Exchange the code for a session token This approach is necessary because WebAuthn is a browser standard and requires browser APIs that aren't directly available in native apps.busy-salesmen-35214
04/28/2025, 6:24 AMbrief-egg-61061
04/28/2025, 6:24 AMbrief-egg-61061
04/28/2025, 6:24 AMRegistrationFlow
that you can use in your Android application: kotlin data class RegistrationFlow( val id: String, val type: String, val expires_at: String, val issued_at: String, val request_url: String, val ui: UiContainer, val active: String? = null, val oauth2_login_challenge: String? = null, val methods: Map<String, MethodConfig>? = null ) data class UiContainer( val action: String, val method: String, val nodes: List<UiNode> ) data class UiNode( val type: String, val group: String, val attributes: UiNodeAttributes, val messages: List<UiNodeMessage> = emptyList(), val meta: Map<String, Any>? = null ) data class UiNodeAttributes( val name: String, val type: String, val value: String? = null, val required: Boolean = false, val disabled: Boolean = false, val node_type: String, val autocomplete: String? = null ) data class UiNodeMessage( val id: Long, val text: String, val type: String, val context: Map<String, Any>? = null ) data class MethodConfig( val config: Any? = null )
This model corresponds to the registration flow response structure as shown in the [Ory documentation for registration flows](https://www.ory.sh/docs/kratos/bring-your-own-ui/custom-ui-basic-integration#create-registration-flow). You can use this model with Retrofit to deserialize the response from the Ory API when creating a registration flow: kotlin interface OryService { @GET("/self-service/registration/api") suspend fun createRegistrationFlow( @Query("return_session_token_exchange_code") returnCode: Boolean = true ): Response<RegistrationFlow> }
The model includes all the essential fields needed to handle registration in your native Android application, including support for different authentication methods like password, WebAuthn/passkeys, and social sign-in.busy-salesmen-35214
04/28/2025, 8:51 AMbrief-egg-61061
04/28/2025, 8:51 AMbrief-egg-61061
04/28/2025, 8:51 AMory patch identity-config <your-project-id> \ --add '/selfservice/methods/passkey/enabled=true' \ --add '/selfservice/methods/passkey/config/rp/display_name="My Display Name"'
2. Configuration files: For self-hosted Ory Kratos, you can directly edit the configuration files: yaml selfservice: methods: passkey: enabled: true config: rp: display_name: Your Application name # Set 'id' to the top-level domain id: localhost # Set 'origin' to the exact URL of the page that prompts the user to use WebAuthn origins: - <http://localhost:4455>
3. API-based configuration: You can also use Ory's configuration APIs to update settings programmatically. For implementing passkeys in your native Android app, the approach remains the same regardless of how you configure Ory - you'll still need to: 1. Create a registration/login flow 2. Open a system browser for WebAuthn/passkey operations 3. Handle the redirect back to your app 4. Exchange the code for a session token The key point is that WebAuthn/passkeys require browser capabilities, so the system browser approach is necessary regardless of how you configure your Ory project. Is there a specific aspect of the configuration or implementation you'd like me to elaborate on?busy-salesmen-35214
04/28/2025, 9:39 AMbrief-egg-61061
04/28/2025, 9:39 AMbrief-egg-61061
04/28/2025, 9:40 AM$oryKratosEndpoint/self-service/registration?flow=$flowId
, you're getting a JSON response instead of the expected passkey registration functionality. This happens because you're using the API endpoint directly in the browser, rather than a UI endpoint that would render the WebAuthn/passkey interface. Here's how to fix this: 1. You need to use the UI URL configured for registration, not the API endpoint: kotlin // Instead of: val webAuthnUrl = "$oryKratosEndpoint/self-service/registration?flow=$flowId" // Use: val webAuthnUrl = "$oryKratosEndpoint/ui/registration?flow=$flowId"
If you're self-hosting Ory Kratos and don't have a UI endpoint configured, you'll need to: 1. Configure a UI URL in your Kratos configuration: yaml selfservice: flows: registration: ui_url: <http://your-ui-domain.com/registration>
2. Implement a UI that can handle the WebAuthn registration process. This UI will receive the flow ID, fetch the flow data, and render the appropriate interface for passkey registration. If you're seeing a JSON response in the browser, it means: 1. Either you're hitting the API endpoint directly 2. Or you haven't configured a UI URL in your Kratos setup For a complete implementation, you should: 1. Create the registration flow using the API endpoint 2. Open the system browser with the UI URL (not the API endpoint) 3. Ensure your UI implementation properly handles WebAuthn/passkey registration 4. Handle the redirect back to your app after successful registration Remember that the WebAuthn functionality requires JavaScript in the browser to work properly, which is why you need a proper UI implementation, not just the raw API endpoint. [Form rendering](https://www.ory.sh/docs/kratos/self-service#form-rendering) in the Ory documentation explains how the UI should use the flow ID to fetch and render the appropriate form. Let me know if you need more specific guidance on implementing the UI part!busy-salesmen-35214
04/28/2025, 10:30 AMbrief-egg-61061
04/28/2025, 10:30 AMbrief-egg-61061
04/28/2025, 10:30 AMyaml selfservice: methods: passkey: enabled: true config: rp: display_name: Your Application name id: localhost origins: - <http://localhost:4455>
2. No passkeys are registered for the user account you're trying to log in with. Passkey login will only appear as an option if: - The user has previously registered a passkey with this account - The browser/device can detect a compatible passkey 3. Browser compatibility issues - not all browsers fully support passkeys. Make sure you're using a modern browser that supports the WebAuthn standard. 4. Configuration issues with WebAuthn - the rp.id
(Relying Party ID) must match the domain you're using, and the origins
must include the exact URL of your login page. To troubleshoot: 1. First, check if passkeys are properly enabled in your configuration 2. Try registering a passkey first (if you haven't already) before attempting to log in with it 3. Verify that your browser supports WebAuthn/passkeys 4. Check the browser console for any JavaScript errors related to WebAuthn If you've confirmed all these settings are correct, you might want to examine the login flow response to see if it includes the passkey method: GET /self-service/login/flows?id=58e5dcfe-4640-4c2b-b914-180d8819c934
The response should include a methods
object with a passkey
property if it's available as an authentication option.busy-salesmen-35214
04/28/2025, 11:48 AMbrief-egg-61061
04/28/2025, 11:48 AMbrief-egg-61061
04/28/2025, 11:48 AMkotlin // For registration with passkey val response = oryService.createRegistrationFlow(returnSessionTokenExchangeCode = true) val flowId = response.body()?.id // For login with passkey val response = oryService.createLoginFlow(returnSessionTokenExchangeCode = true) val flowId = response.body()?.id
2. Then open the system browser with the UI URL that includes this flow ID: kotlin // For a self-hosted Kratos instance val browserUrl = "$oryKratosEndpoint/ui/registration?flow=$flowId" // OR val browserUrl = "$oryKratosEndpoint/ui/login?flow=$flowId" // Open system browser val intent = Intent(Intent.ACTION_VIEW, Uri.parse(browserUrl)) startActivity(intent)
The key point is that you need to: 1. Make sure passkeys are enabled in your Ory configuration 2. Use the UI URL (not the API endpoint) in the system browser 3. Configure your UI to properly handle WebAuthn/passkey functionality If you're seeing only "Send sign in code" and not passkey options, it's likely because: - Passkeys aren't properly enabled in your configuration - No passkeys have been registered for the account - The UI implementation doesn't properly support passkeys Remember that WebAuthn/passkeys require browser capabilities and JavaScript, which is why you need to use a system browser rather than trying to implement this directly in your native app. Is there a specific issue you're encountering with the current approach?