{"id":15051291,"url":"https://github.com/nextcloud/android-singlesignon","last_synced_at":"2025-04-05T20:08:22.665Z","repository":{"id":17717721,"uuid":"20541970","full_name":"nextcloud/Android-SingleSignOn","owner":"nextcloud","description":"Single sign-on for Nextcloud (Android Library Project)","archived":false,"fork":false,"pushed_at":"2025-04-04T16:26:39.000Z","size":4499,"stargazers_count":70,"open_issues_count":25,"forks_count":32,"subscribers_count":19,"default_branch":"main","last_synced_at":"2025-04-05T20:08:17.420Z","etag":null,"topics":["android","android-library","nextcloud"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/nextcloud.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2014-06-05T21:19:34.000Z","updated_at":"2025-04-01T13:35:29.000Z","dependencies_parsed_at":"2024-05-21T11:44:12.885Z","dependency_job_id":"9f7f413c-a8bf-4858-9f49-f42c2655a850","html_url":"https://github.com/nextcloud/Android-SingleSignOn","commit_stats":null,"previous_names":[],"tags_count":37,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nextcloud%2FAndroid-SingleSignOn","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nextcloud%2FAndroid-SingleSignOn/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nextcloud%2FAndroid-SingleSignOn/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nextcloud%2FAndroid-SingleSignOn/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nextcloud","download_url":"https://codeload.github.com/nextcloud/Android-SingleSignOn/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247393570,"owners_count":20931813,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["android","android-library","nextcloud"],"created_at":"2024-09-24T21:32:58.466Z","updated_at":"2025-04-05T20:08:22.634Z","avatar_url":"https://github.com/nextcloud.png","language":"Java","readme":"\u003c!--\n ~ SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors\n ~ SPDX-License-Identifier: GPL-3.0-or-later\n--\u003e\n# \u003cimg src=\"https://github.com/nextcloud/Android-SingleSignOn/raw/main/.idea/icon.svg\" width=\"24\" height=\"24\" alt=\"Nextcloud Single Sign On Logo\"/\u003e Nextcloud Single Sign On\n\n[![Codacy Badge](https://app.codacy.com/project/badge/Grade/3fe63bb2932243f08dc362fa49c5275b)](https://app.codacy.com/gh/nextcloud/Android-SingleSignOn/dashboard?utm_source=gh\u0026utm_medium=referral\u0026utm_content=\u0026utm_campaign=Badge_grade)\n[![Last release](https://jitpack.io/v/nextcloud/Android-SingleSignOn.svg)](https://jitpack.io/#nextcloud/Android-SingleSignOn)\n[![GitHub issues](https://img.shields.io/github/issues/nextcloud/Android-SingleSignOn.svg)](https://github.com/nextcloud/Android-SingleSignOn/issues)\n[![GitHub stars](https://img.shields.io/github/stars/nextcloud/Android-SingleSignOn.svg)](https://github.com/nextcloud/Android-SingleSignOn/stargazers)\n[![License: GPL v3](https://img.shields.io/badge/License-GPL%20v3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0)\n[![REUSE status](https://api.reuse.software/badge/github.com/nextcloud/Android-SingleSignOn)](https://api.reuse.software/info/github.com/nextcloud/Android-SingleSignOn)\n\nThis library allows you to use accounts as well as the network stack provided by the [Nextcloud Files app](https://play.google.com/store/apps/details?id=com.nextcloud.client). Therefore you as a developer don't need to worry about asking the user for credentials as well as you don't need to worry about self-signed ssl certificates, two factor authentication, save credential storage etc.\n\n*Please note that the user needs to install the [Nextcloud Files app](https://play.google.com/store/apps/details?id=com.nextcloud.client) in order to use those features.* While this might seem like a \"no-go\" for some developers, we still think that using this library is worth consideration as it makes the account handling much faster and safer.\n\n- [How to use this library](#how-to-use-this-library)\n  - [1) Add this library to your project](#1-add-this-library-to-your-project)\n  - [2) To choose an account, include the following code in your login dialog](#2-to-choose-an-account-include-the-following-code-in-your-login-dialog)\n  - [3) To handle the result of the Account Chooser, include the following](#3-to-handle-the-result-of-the-account-chooser-include-the-following)\n  - [4) How to get account information?](#4-how-to-get-account-information)\n  - [5) How to make a network request?](#5-how-to-make-a-network-request)\n    - [5.1) Using Retrofit](#51-using-retrofit)\n    - [5.2) Without Retrofit](#52-without-retrofit)\n    - [5.3) WebDAV](#53-webdav)\n- [Additional info](#additional-info)\n- [R8/ProGuard](#r8proguard)\n- [Security](#security)\n- [Media](#media)\n  - [Talks at the Nextcloud Conference](#talks-at-the-nextcloud-conference)\n  - [Demo video](#demo-video)\n- [Known apps](#known-apps)\n- [Troubleshooting](#troubleshooting)\n- [Flow diagram](#flow-diagram)\n- [Translations](#translations)\n\n## How to use this library\n\nYou can check out the [sample app](https://github.com/nextcloud/Android-SingleSignOn/tree/master/sample) which uses this library to fetch some information via SSO from a Nextcloud instance.\nThe sample app uses the [Retrofit approach](#51-using-retrofit). Be aware though, that it is for demonstration purposes only. Exception handling, state management etc. must be implemented depending on your use case.\n\n### 1) Add this library to your project\n\n```gradle\nrepositories {\n    // …\n    maven { url \"https://jitpack.io\" }\n}\n\ndependencies {\n    // Note: Android Gradle Plugin (AGP) version ≥ 8.2.0 is required.\n    implementation \"com.github.nextcloud:Android-SingleSignOn:1.0.0\"\n}\n```\n\n### 2) To choose an account, include the following code in your login dialog\n\n```java\nprivate void openAccountChooser() {\n    try {\n        AccountImporter.pickNewAccount(activityOrFragment);\n    } catch (NextcloudFilesAppNotInstalledException | AndroidGetAccountsPermissionNotGranted e) {\n        UiExceptionManager.showDialogForException(this, e);\n    }\n}\n```\n\n### 3) To handle the result of the Account Chooser, include the following\n\n```java\n@Override\npublic void onActivityResult(int requestCode, int resultCode, Intent data) {\n    super.onActivityResult(requestCode, resultCode, data);\n    AccountImporter.onActivityResult(requestCode, resultCode, data, this, new AccountImporter.IAccountAccessGranted() {\n\n        @Override\n        public void accountAccessGranted(SingleSignOnAccount account) {\n            final var context = getApplicationContext();\n\n            // As this library supports multiple accounts we created some helper methods if you only want to use one.\n            // The following line stores the selected account as the \"default\" account which can be queried by using\n            // the SingleAccountHelper.getCurrentSingleSignOnAccount(context) method\n            SingleAccountHelper.commitCurrentAccount(context, account.name);\n\n            // Get the \"default\" account\n            SingleSignOnAccount ssoAccount = null;\n            try {\n                ssoAccount = SingleAccountHelper.getCurrentSingleSignOnAccount(context);\n            } catch (NextcloudFilesAppAccountNotFoundException | NoCurrentAccountSelectedException e) {\n                UiExceptionManager.showDialogForException(context, e);\n            }\n\n            final var nextcloudAPI = new NextcloudAPI(context, ssoAccount, new GsonBuilder().create());\n\n            // TODO … (see code in section 4 and below)\n        }\n    });\n}\n\n@Override\npublic void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {\n    super.onRequestPermissionsResult(requestCode, permissions, grantResults);\n    AccountImporter.onRequestPermissionsResult(requestCode, permissions, grantResults, this);\n}\n\n// Complete example: https://github.com/nextcloud/news-android/blob/890828441ba0c8a9b90afe56f3e08ed63366ece5/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/LoginDialogActivity.java#L470-L475\n\n```\n\n### 4) How to get account information?\n\n```java\n// If you stored the \"default\" account using setCurrentAccount(…) you can get the account by using the following line:\nfinal var ssoAccount = SingleAccountHelper.getCurrentSingleSignOnAccount(context);\n\n// It is also possible to get the \"default\" account as a LiveData object:\nfinal var ssoAccount$ = SingleAccountHelper.getCurrentSingleSignOnAccount$(context);\n\n// Otherwise (for multi-account support you'll have to keep track of the account names yourself. Note: this has to be the name of SingleSignOnAccount.name)\nAccountImporter.getSingleSignOnAccount(context, accountName);\n\nssoAccount.name; // Name of the account used in the android account manager\nssoAccount.username;\nssoAccount.token;\nssoAccount.url;\n```\n\n### 5) How to make a network request?\n\n```java\npublic NextcloudAPI(Context context, SingleSignOnAccount account, Gson gson) {\n```\n\nYou'll notice that there is an optional `ApiConnectedListener` callback parameter in the constructor of the `NextcloudAPI`.\nYou can use this callback to subscribe to errors that might occur during the initialization of the API.\nThe callback method `onConnected` will be called once the connection to the files app is established.\n\nℹ️ You can start making requests to the API before that callback is fired as the library will queue your calls until the connection is established[¹](https://github.com/nextcloud/Android-SingleSignOn/issues/400).\n\n#### 5.1) **Using Retrofit**\n\n##### 5.1.1) Before using this Single Sign On library, your interface for your [Retrofit](https://square.github.io/retrofit/) API might look like this:\n\n```java\npublic interface API {\n\n    String mApiEndpoint = \"/index.php/apps/news/api/v1-2/\";\n\n    @GET(\"user\")\n    Observable\u003cUserInfo\u003e user();\n\n    // use ParsedResponse, in case you also need the response headers. Works currently only for Observable calls.\n    @GET(\"user\")\n    Observable\u003cParsedResponse\u003cUserInfo\u003e\u003e user();\n\n    @POST(\"feeds\")\n    Call\u003cList\u003cFeed\u003e\u003e createFeed(@Body Map\u003cString, Object\u003e feedMap);\n\n    @DELETE(\"feeds/{feedId}\")\n    Completable deleteFeed(@Path(\"feedId\") long feedId);\n\n    // …\n}\n```\n\nℹ️ If your REST endpoint returns an empty body, you need to specify `Observable\u003cEmptyResponse\u003e` / `Call\u003cEmptyResponse\u003e` as return value rather than `Observable\u003cVoid\u003e` / `Call\u003cVoid\u003e` because [\"Nulls are not allowed in \\[RxJava\\] 2.x.\"](https://github.com/ReactiveX/RxJava/issues/5775#issuecomment-353544736).\n\nYou might instantiate your Retrofit `API` by using something like this:\n\n```java\npublic class ApiProvider {\n\n    private final API mApi;\n\n    public ApiProvider() {\n        mApi = retrofit.create(API.class);\n    }\n}\n```\n\n##### 5.1.2) Use of new API using the nextcloud app network stack\n\n```java\npublic class ApiProvider {\n\n    private final API mApi;\n\n    public ApiProvider(@NonNull NextcloudAPI.ApiConnectedListener callback) {\n       final var ssoAccount = SingleAccountHelper.getCurrentSingleSignOnAccount(context);\n       final var nextcloudAPI = new NextcloudAPI(context, ssoAccount, new GsonBuilder().create(), callback);\n       mApi = new NextcloudRetrofitApiBuilder(nextcloudAPI, API.mApiEndpoint).create(API.class);\n   }\n}\n```\n\nEnjoy! If you're already using Retrofit, you don't need to modify your application logic. Just exchange the API and you're good to go!\n\nℹ️ If you need a different mapping between your JSON structure and your Java structure you might want to create a custom type adapter using `new GsonBuilder().create().registerTypeAdapter(…)`. Take a look at [this](https://github.com/nextcloud/news-android/blob/783836390b4c27aba285bad1441b53154df16685/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/helper/GsonConfig.java) example for more information.\n\n#### 5.2) **Without Retrofit**\n\n`NextcloudAPI` provides a method called `performNetworkRequest(NextcloudRequest request)` that allows you to handle the server response yourself.\n\n```java\npublic class MyActivity extends AppCompatActivity {\n\n    private NextcloudAPI mNextcloudAPI;\n\n    @Override\n    protected void onStart() {\n        super.onStart();\n        try {\n            final var ssoAccount = SingleAccountHelper.getCurrentSingleSignOnAccount(this);\n            mNextcloudAPI = new NextcloudAPI(this, ssoAccount, new GsonBuilder().create());\n\n            // Start download of file in background thread (otherwise you'll get a NetworkOnMainThreadException)\n            new Thread(this::downloadFile).start();\n        } catch (NextcloudFilesAppAccountNotFoundException | NoCurrentAccountSelectedException e) {\n            // TODO handle errors\n        }\n    }\n\n    @Override\n    protected void onStop() {\n        super.onStop();\n        // Close Service Connection to Nextcloud Files App and\n        // disconnect API from Context (prevent Memory Leak)\n        mNextcloudAPI.close();\n    }\n\n    private void downloadFile() {\n        final List\u003cPair\u003cString, String\u003e\u003e parameters = new ArrayList\u003c\u003e();\n        parameters.add(new QueryPair(\"quality\", \"1024p\"));\n        parameters.add(new Pair\u003c\u003e(\"someOtherParameter\", \"parameterValue\"));\n        \n        final var nextcloudRequest = new NextcloudRequest.Builder()\n                .setMethod(\"GET\")\n                .setParameter(parameters)\n                .setUrl(Uri.encode(\"/remote.php/webdav/sample movie.mp4\",\"/\"))\n                .build();\n\n        try (final var inputStream = mNextcloudAPI.performNetworkRequest(nextcloudRequest)) {\n            while(inputStream.available() \u003e 0) {\n                inputStream.read();\n                // TODO do something useful with the data here..\n                // like writing it to a file…?\n            }\n        } catch (Exception e) {\n            // TODO handle errors\n        }\n    }\n}\n```\n\n#### 5.3) **WebDAV**\n\nCurrently the following `WebDAV` Methods are supported: `PROPFIND` / `MKCOL`\n\nThe following examples shows how to use the `PROPFIND` method with a depth of 0.\n\n```java\nfinal List\u003cString\u003e depth = new ArrayList\u003c\u003e();\ndepth.add(\"0\");\nheader.put(\"Depth\", depth);\n\nfinal var nextcloudRequest = new NextcloudRequest.Builder()\n        .setMethod(\"PROPFIND\")\n        .setHeader(header)\n        .setUrl(Uri.encode(\"/remote.php/webdav/\" + remotePath, \"/\"))\n        .build();\n```\n\n## Additional info\n\nIn case that you require some SSO features that were introduced in a specific Nextcloud Files app version, you can run a simple version check using the following helper method:\n\n```java\nfinal int MIN_NEXTCLOUD_FILES_APP_VERSION_CODE = 30030052;\n\nif (VersionCheckHelper.verifyMinVersion(context, MIN_NEXTCLOUD_FILES_APP_VERSION_CODE, FilesAppType.PROD)) {\n   // Version requirement is satisfied!\n}\n```\n\n## R8/ProGuard\n\nR8 and ProGuard rules are bundled into [SSO](lib/consumer-proguard-rules.pro).\nThe bundled rules do **not** cover enabled obfuscation.\nTherefore it is **recommended** to add `-dontobfuscate` to your app-specific proguard rules.\n\nWith [R8 full mode](https://r8.googlesource.com/r8/+/refs/heads/master/compatibility-faq.md#r8-full-mode) being enabled by default since [AGP 8.0](https://developer.android.com/build/releases/gradle-plugin#default-changes), you will probably need to handle following app-specific rules yourself (or disable full mode):\n\n### Gson\nAccording to [Gson's sample rules](https://github.com/google/gson/blob/master/examples/android-proguard-example/proguard.cfg#L14), you still need to configure rules for your gson-handled classes.\n\u003e ```\n\u003e # Application classes that will be serialized/deserialized over Gson\n\u003e -keep class com.google.gson.examples.android.model.** { \u003cfields\u003e; }\n\u003e ```\n\n### Retrofit\nThe same applies to classes which you're using in the api from step [5.1.1](#511-before-using-this-single-sign-on-library-your-interface-for-your-retrofit-api-might-look-like-this)\n```\n# Application classes that will be serialized/deserialized by Retrofit\n-keep class com.google.gson.examples.android.model.**\n```\n\nIf you find working less broad rules, contributions to these rules are welcome!\n\n## Security\n\nOnce the user clicks on \u003ckbd\u003eAllow\u003c/kbd\u003e in the login dialog, the Nextcloud Files App will generate a token for your app. Only your app is allowed to use that token. Even if another app will get a hold of that token, it won't be able to make any requests to the nextcloud server as the nextcloud files app matches that token against the namespace of your app.\n\n![](doc/NextcloudSSO.png)\n\n![](doc/NextcloudSSOHacker.png)\n\n## Media\n\n### Talks at the Nextcloud Conference\n\n| 2018 (5min) | 2020 (5min) |\n| --- | --- |\n| [![Nextcloud Single Sign On for Android David Luhmer](https://img.youtube.com/vi/gnLOwmrJLUw/0.jpg)](https://www.youtube.com/watch?v=gnLOwmrJLUw) | [![Nextcloud Single Sign On for Android David Luhmer](https://i.ytimg.com/vi/oQJWAv2wVuc/hqdefault.jpg)](https://www.youtube.com/watch?v=oQJWAv2wVuc) |\n\n### Demo video\n\n![Demo video](https://user-images.githubusercontent.com/4489723/41563281-75cbc196-734f-11e8-8b22-7b906363e34a.gif)\n\n## Known apps\n\n- [Nextcloud News app](https://github.com/nextcloud/news-android)\n  - [API](https://github.com/nextcloud/news-android/blob/master/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/reader/nextcloud/NewsAPI.java)\n  - [API-Provider (Dagger)](https://github.com/nextcloud/news-android/blob/master/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/di/ApiProvider.java#L105-L114)\n  - [Login Activity](https://github.com/nextcloud/news-android/blob/master/News-Android-App/src/main/java/de/luhmer/owncloudnewsreader/LoginDialogActivity.java)\n- [Nextcloud Notes app](https://github.com/stefan-niedermann/nextcloud-notes)\n  - [API](https://github.com/stefan-niedermann/nextcloud-notes/blob/master/app/src/main/java/it/niedermann/owncloud/notes/persistence/ApiProvider.java#L85-L106)\n  - [Login](https://github.com/stefan-niedermann/nextcloud-notes/blob/master/app/src/main/java/it/niedermann/owncloud/notes/shared/util/SSOUtil.java#L33)\n- [Nextcloud Deck app](https://github.com/stefan-niedermann/nextcloud-deck/)\n  - [API](https://github.com/stefan-niedermann/nextcloud-deck/blob/master/app/src/main/java/it/niedermann/nextcloud/deck/api/DeckAPI.java)\n  - [Login](https://github.com/stefan-niedermann/nextcloud-deck/blob/master/app/src/main/java/it/niedermann/nextcloud/deck/ui/ImportAccountActivity.java#L77)\n- [Nextcloud Bookmarks app](https://gitlab.com/bisada/OCBookmarks)\n  - [API](https://gitlab.com/bisada/OCBookmarks/-/blob/master/app/src/main/java/org/schabi/ocbookmarks/REST/OCBookmarksRestConnector.java#L42)\n  - [Login](https://gitlab.com/bisada/OCBookmarks/-/blob/master/app/src/main/java/org/schabi/ocbookmarks/MainActivity.java#L261)\n\n\n## Troubleshooting\n\nIf you are experiencing any issues, the following tips might workaround:\n- Disable battery optimizations of the Nextcloud Files app, especially [in case of a `NextcloudApiNotRespondingException`](https://github.com/nextcloud/Android-SingleSignOn/issues/162)\n- [Permit auto start](https://github.com/stefan-niedermann/nextcloud-deck/issues/660#issuecomment-682002392)\n- A quickly appearing and disappearing menu when attempting to select an account is often a hint for an outdated Nextcloud Files app\n\n## Flow Diagram\n\nNote that the \"Make network request\" section in the diagram only shows the workflow if you use the Retrofit API.\n\n![Flow Diagram](doc/NextcloudSingleSignOn.png)\n\n# Translations\n\nWe manage translations via [Transifex](https://app.transifex.com/nextcloud/nextcloud/android-singlesignon/). So just request joining the translation team for Android on the site and start translating. All translations will then be automatically pushed to this repository, there is no need for any pull request for translations.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnextcloud%2Fandroid-singlesignon","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnextcloud%2Fandroid-singlesignon","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnextcloud%2Fandroid-singlesignon/lists"}